哈,喷下面向对象与c

中山野鬼 发布于 2015/04/02 22:25
阅读 1K+
收藏 4

正好写书稿,写到这段。喷面向对象与C不是说反对面向对象。相反,我一直坚持采用面向对象的分析方法去设计C。下面抄一段书稿的内容。算是先喷喷。只是初稿,语句不通顺,含义表达不清楚的地方,就凑合这看看吧。哈。

============ 如下===============

对于C语言而言,数据对象的类型,本身就是一种抽象的描述。它并没有确定具体的存储空间,但它描述了一个存储空间的组织结构,如果它是基础类型,还明确了对应的操作方式,而不用关心具体对什么存储空间进行操作。也即,类型本身,便是对对象的一种抽象描述。
除了一些特定的设计目标,在已有设计成果中文字描述的处理对象很少对应基础类型。通常对应的是结构体类型。每一类的对象包含多种属性/特征,我们对它们的属性/特征进行设置、检测、比较、统计来完成一些业务子逻辑。那么对于业务中某类待处理对象的属性/特征及其基础操作,则是我们首先需要用C语言代码来描述的。由于它们不对应具体的数据对象,也不对应具体的对象类别,因此它们是一种抽象的描述。例如上节中的举例,这里重复如下:

typedef struct{
  int x;
  int y;
}_XY;
typedef struct{
  int x;
  int y;
  int z;
}_XYZ;

#define _getX(p) (p)->x
#define _getY(p) (p)->y

虽然存在两种类别对象,但它们包含相同的属性/特征。那么上面的两个宏,则是对一(大)类,具有相同属性/特征的对象类型的特定属性/特征的获取操作的描述。

上述这句话有点绕,或许对比的理解可能更容易。
类似C++的面向对象的语言,(注意此处的面向对象,与采用对象化的分析方法,也即本章所讨论的面向对象不是同一个概念)可能会定义_XY是一个类,并作为_XYZ的父类。x,y和z,在_XYZ中存在层级的差异。

而此处我们讨论的内容是,存在两个对象类别,它们具有相同的属性/特征,所以将这两个类别,看作同一个大类(该大类的区分,以是否存在相同属性/特征来判定)。那么上面的两个宏,可通过不同的具体化操作,对应到上述两个数据类别中。而不是说这两个宏,对应某个大类的操作,而这个大类是上述两个类的父类。

两种完全不同类别的数据对象,因为存在相同的属性/特征,所以针对这些属性/特征及其操作我们看作为一(大)类去独立的定义出两个操作的宏。而在业务逻辑(及其分析内容)中更本不存在某个大类的类型。你为了抽象的描述,而额外具体化出来,并把它当作另两个结构体类型的成员以近似看作是后两者的父类,这等同“脱裤子放屁”,虽然话很糙,但其毫无意义的行为结果是相同的,会熏得你晕头转向。 

(注意,此处并不是说类似c++的面向对象语言的“类”设计方法论存在问题,纯粹的理论不存在对错,如果一个业务逻辑中处理的对象,本身具备类与类的关联和继承内容,采用这些“类”设计方法,会极大的提高编程实现效率,而如果它们并没有这些本质关联,仅仅是形式上可以如此表达,那么这种额外的形式化设计,只会把简单点事情复杂化)。
对于上面的举例,如下的代码设计方式,基于业务中并不存在_2D这种类型的对象,则是一种错误的抽象描述:
typedef struct{
  int x;
  int y;
}_2D;
typedef struct{
  _2D xy[1];
}_XY;
typedef struct{
  _2D xy[1];
  int z;
}_XYZ;

首先,上述代码不是抽象的描述。因为所有类型均已经明确。其次,你额外增加的内容,并不对应文字型设计成果中的“把x,y复制过来”这句抽象描述。因为你会存在类似这样的内容:

_XY d2[1];
_XYZ d3[1];
#define _copy2D(p1,p2) do{_getX(p1) = _getX(p2);_getY(p1) = _getY(p2);}while (0)


_copy2D(d2->xy,d3->xy);

此处_copy2D的含义是对一个结构体连续空间的内容进行复制。如果你还不理解父类继承与C语言抽象的描述的差异,我们可以如下做个举例:

typedef struct{
  int x;
  int y;
}_XY;
typedef struct{
  int z;
  int y;
}_YZ;
typedef struct{
  int z;
  int x;
}_XZ;
typedef struct{
  int x;
  int y;
  int z;
}_XYZ;

#define _getX(p) (p)->x
#define _getY(p) (p)->y
#define _getZ(p) (p)->z
#define _copyXY(p1,p2) do{_getX(p1) = _getX(p2);_getY(p1) = _getY(p2);}while (0)
#define _copyYZ(p1,p2) do{_getZ(p1) = _getZ(p2);_getY(p1) = _getY(p2);}while (0)
#define _copyXZ(p1,p2) do{_getZ(p1) = _getZ(p2);_getX(p1) = _getX(p2);}while (0)

对于上述3个_copy开头的抽象描述,你采用定义一个明确类型_2D,作为不同结构体成员的类型的设计方式,是否悲剧了?

其实上述的内容还可以继续抽象如下: 
#define _copy2D(p1,p2,_get1,_get2) do{_get1(p1) = _get1(p2);_get2(p1) = _get2(p2);}while (0)
#define _copyXY(p1,p2) _copy2D(p1,p2,_getX,_getY)
#define _copyYZ(p1,p2) _copy2D(p1,p2,_getZ,_getY)
#define _copyXZ(p1,p2) _copy2D(p1,p2,_getX,_getZ)

上述抽象描述的含义如下:

_copy2D:将两个数据的两个对应维度的数据进行复制。后者复制给前者。
_copyXY: 将两个数据指定的x,y维度进行复制
_copyYZ,_copyXZ等同描述。
实际上,你会发现,最后一句文字描述的内容(_copyYZ,_copyXZ等同描述),本身就是一种抽象,在我们的设计成果(非代码文档)中,经常会存在这样的内容。可能当前具体化的设计时,你不存在z或者不存在y,这没有关系,不影响你对上述宏定义的具体化利用。相反,如果你具体化的设计内容中,x,y,z均存在,和已有设计成果完全等同,那么就属于代码的直接利用(无论你是采用类似c++这样的语言,还是类似c这样的语言),这种代码直接利用的方式,和本章所讨论的抽象描述并不是一回事。
简单的小结一下,值得我们抽象描述的是已有设计成果中对结构体成员的操作。也因此,给出个建议,无论你定义什么样的结构体,针对该结构体成员的获取操作,均定义成类似_getX(p)这样的宏。通过宏名称,去确定未知结构体类型中特定成员的具体含义。这就非常有利于我们下面要讨论的抽象描述内容,也即操作逻辑的抽象。
操作和操作逻辑,如果非要细究还是有点点区别。简单说,后者包含了不与具体结构特性关联的处理逻辑。
#define _getX(p) (p)->x
本身是一个操作,你也可以说,它中间带有逻辑,毕竟x成员的存储位置,需要依据p指向的位置基于一定的计算逻辑来获取。但是这样的逻辑和该成员所在空间的结构类型相关。而如下的宏定义则和其存在空间的结构特性没有关联:
#define _chkX(p) (_getX(p))
它的主体逻辑,是对一个值做判断。虽然利用了_getX,但究竟怎么取到这个数据的数值,不是这个宏定义所对应的描述内容。如果我们在持续针对某一类业务进行设计时,将各个业务中的术语统一起来,那么很多原子对象(不可细分)的术语,在代码构造中,可通过统一的名称对应,无论它属于什么结构体,均表示相同的含义。这样再不同的业务设计任务中,由于成员名称与某个术语一一对应,而术语在一个业务背景域中的含义是不变的,则基于该术语的操作及操作逻辑便可通过C语言宏定义的方式,抽象的描述出来,这样,在你的新设计任务中,只要针对该术语的操作设计变可省去。
这里先给出第三节,索引模块中的一段代码的举例:
#define _Inext(pI,i) ((pI) + i)->_next
#define _Iprec(pI,i) ((pI) + i)->_prec

如果我们在不同的数据对象定义时,都确定,_next仅仅用于描述指向下一个节点的存储下标,那么无论数据对象的具体结构是什么,你都可以通过如下方式来针对循环链进行遍历。

#define _travell(pI,cur,op,...) do{\
_I __cur = cur;\
do {\
op(__VA_ARGS__);\
__cur = _Inext(pI,__cur);\
}while(__cur != cur);\
}while (0)

在本书中我反复在强调,相对业务的理解,c语言的设计技巧不足为道。上述的讨论,我更希望你能理解一个道理:

已有(非代码)设计成果,最大化的复用,它的前提是你对业务的充分理解,在你设计中,能准确的定义出那些含义不变的术语,以及能从复杂的业务逻辑中,抽象出共性内容,并用文字进行描述。没有这些前提工作,使用任何C语言的设计技巧,你也无法构造出可充分复用的抽象代码。

==========文稿部分内容结束=======

加载中
0
cwhow
cwhow
mark 下
0
G_Young
G_Young
要出书了?
0
m
magiclogy

作为面向对象语言的用户,我觉得你这是在怀疑大家都不会用面向对象语言。

简单的小结一下,值得我们抽象描述的是已有设计成果中对结构体成员的操作。
不就是抽象出来的是虚函数(性能第一的时候,STL的处理方式可以解决这个问题)。

子类和超类的关系,正常情况下是“子类是一个超类”。本着这个观点,_XY是一个_2D,_XYZ是一个_2D,后者的关系,完全不靠谱,_XYZ包含_2D还差不多。。。

继承的关键在于多态行为而不是重用代码
_XY和_XYZ可以有共同的超类,一种可以投影到OXY平面上的点,因而存在共同的操作:计算出在OXY平面上的坐标getXY()。

最后,对于复用这个东西啊,我只能说,根据需要复用的程度,选一种合适的方案吧。。。。。

0
asdfsx
asdfsx
我要来求书!!!
0
中山野鬼
中山野鬼

引用来自“magiclogy”的评论

作为面向对象语言的用户,我觉得你这是在怀疑大家都不会用面向对象语言。


简单的小结一下,值得我们抽象描述的是已有设计成果中对结构体成员的操作。
不就是抽象出来的是虚函数(性能第一的时候,STL的处理方式可以解决这个问题)。

子类和超类的关系,正常情况下是“子类是一个超类”。本着这个观点,_XY是一个_2D,_XYZ是一个_2D,后者的关系,完全不靠谱,_XYZ包含_2D还差不多。。。


继承的关键在于多态行为而不是重用代码
_XY和_XYZ可以有共同的超类,一种可以投影到OXY平面上的点,因而存在共同的操作:计算出在OXY平面上的坐标getXY()。

最后,对于复用这个东西啊,我只能说,根据需要复用的程度,选一种合适的方案吧。。。。。

哈,这里我谈的c语言,c 里面没有你说的这类的东西,我仅仅是讨论,基于面向对象的分析方法,如何使用c语言去描述抽象的设计内容。而不是去讨论某个面向对象语言,基于其语法,在形式化上的设计实现方法。至于你说的“超类”等,都是语言语法的形式化。如果c非要这么做,也可以通过额外做个预编译器,扩展这些语法规则,可是又何必呢?“简单的事情,会搞的很复杂”。

不过你的观点有一点我非常认同。我在后续内容中也强调,任何设计方法,只是一种逻辑表达的形式,关键要看实际设计情况。根据实际情况来决定究竟使用什么逻辑表达的方式。

0
首席搬砖工程师
首席搬砖工程师
你之前不是喷过了吗,现在又来喷
0
纠结名字
我以前的Java老师教第一章的时候说,第一章就简单看看,说是优点,其实也是缺点。学Java的要是不说道说到面向对象,你都不好意思说你学过。同样的,一个用C的,如果不喷喷面向对象,就好像老学究能接受白话文似的。守旧派总是要打压一下新兴异类的,大家都该淡然点。毕竟不喷,楼主写一本书,可能就要少一章内容了。
0
首席撸出血
首席撸出血
看到哈字,我就知道这个文章是中山野鬼写的,哈
0
Liuxd
Liuxd
@中山野鬼 ,出书吧。你出本C相关的书我绝对买正版。我最近正想强攻C。
中山野鬼
中山野鬼
@Liuxd 加快写完吧。一直在努力,不过能力有限,没法一天就是几万字的那么潇洒,对应代码倒是早就好了。。
Liuxd
Liuxd
回复 @中山野鬼 : 所以什么时候出版?
中山野鬼
中山野鬼
回复 @songtzu : 哈,水平有限。不过相对经典书籍。我的角度不一样,完全从工程开发(原型设计阶段)角度考虑c语言的设计问题,而不是针对c语言本身去讨论。说白了,没人买无所谓,留着做自己团队的入职培训教材。
Liuxd
Liuxd
回复 @songtzu : 哈哈,书还没出就被你枪毙了。
songtzu
songtzu
C的经典书一大堆了。野鬼的文笔不理想,估计你买了也是吃灰。
0
滔哥
滔哥
野鬼.出手.我肯定入手.让我儿从识字起就学好C语言!
返回顶部
顶部