我来讨论一下纯理论的老问题.长方形和正方形的继承关系.

07-03-01 siberian
类的定义中,大家一直争吵不休的就是正方形和长方形的关系.所说,正方形不是长方形的子类,因为它违反了长方形的行为.

然而计算机世界应当是当前世界在计算机中的映射.那么我们来考察长方形和正方形的定义,长方形也就是矩形:为四个内角相等的平行四边形。正方形,是平行四边形;. 有一个角是直角;. 有一组邻边相等。

可是oo对象中,却不能是继承关系。为什么?因为里面的概念已经被偷换了,我们所定义的只是我们在某方面运用到的长方形和正方形。它的定义是全新的,包括了里面所有的数据和所有的方法。不再是我们在数学中定义的长方形和正方形。

也就是说我们的oo定义的类,和现实世界不能完全映射上,只是反映其中我们想要得部分。它的定义是不完整的,过于简单,无法反映数据和数据间的关系,契约太过强硬,只要是所用到的行为子类都要遵守。于是我们很着急,这违反了oo初衷。哲学上告诉我们片面的抽象扩大化,会走到我们所期望的反面。可以说这就是一个例子,原本现实分类中很明显的继承关系,反而到了oo中,不成立了,或者说成为一个问题。不象现实中那样清晰。

我们为了简化而把事情搞复杂了。也许我们在纯粹的程序世界里面还可以不大在意这件事,按照自己定义的就可以了。但是我们要做需求分析时,这就成为大问题。那是必须和现实世界映射上的,任何的扭曲都会带来很多的麻烦。现在工作中我们的麻烦确实很多,原本清晰可见的关系,我们要重新调理。于是我们开始思考。

我们要用现实世界中的关系来指导oo设计,于是什么LSP,DBC都冒出来告诉我们怎么做。可是还是不行。

下一篇讨论这个问题在做用例分析时可能造成的泥潭。如何回避。

在我的blog:http://yingyiyy.blog.163.com/有后续文章。讲谈到这在工作中的实际意义。

5
Coolyu0916
2007-03-01 20:24
我不知道谁讨论说正方形不是长方形的子类的

你能把文章给我转贴一下么??

我觉得既然从数学定义上是,实际设计也一定是。

要不然oo的设计就是个错误

checkcode
2007-03-01 21:53
我觉得可能是这个问题,当

正方形继承长方形的时候,可以使正方形is a长方形

但是无法保证,x形继承正方形时,使x形既具有正方形的行为又具有长方形的行为

Coolyu0916
2007-03-01 22:11
没有明白楼上的意思

x继承了正方形必然具备正方形的特性

所谓继承的意思就是 XX就是XX

男人就是人 ,女人是人

人生孩子是错的

必须是女人生孩子

楼主是说 老女人不具备生孩子的方法么,他具备这个方法,但是返回的东西不同??

可是这在生活中也是必然的

if(age > 60) return null; 或者throw excption(“年纪太大”)

不是这样么??

siberian
2007-03-01 22:48
其实,这在《java编程思想》里面认为是可以成立的。只不过某些涉及父类的方法要修改。

我认为也是成立的。只不过我们从这个争论为什么会发生,会发现类定义里面类太简单,没有涉及到约束的问题。

这是要说的重点。一个事物必然要包括一个约束问题。也就是说一个类要成功映射。不是他的所有属性,方法都必须被遵守,而是只要它符合一个约束,就成立了。

wlmouse
2007-03-01 23:30
就好像椭圆和圆的关系一样。如果椭圆继承了圆,那么就会产生麻烦。一个圆引用指向一个椭圆的实例。那么给圆半径赋值这个操作到椭圆上面就麻烦了。圆的半径是一样的。椭圆则是不一样的,有一个长的,一个短的。该怎么办?

Coolyu0916
2007-03-02 09:20
to siberian

怎么可能那??

这是一个典型的约束啊

由于一个事务的状态改变而行为不同

这时候使用state模式啊

交给他的state模型来做

30岁的女人做什么

60岁的女人做什么

这些不是约束么??

to wlmouse

园是椭圆的子类

因为所有基于椭圆的数学运算都能运用在园上

我们说 园是一种特殊的椭圆 可是不能说椭圆是一种特殊的圆

checkcode
2007-03-02 11:15
我的意思是继承是is a的关系如果硬要用继承来描述长方形和正方形

那么实际的关系应该是is a而不是like a(正方形就是长方形吗?)

并且在考虑如果又有一个对象继承了正方形,或者继承了长方形,那他

又是什么,?现实世界不可能有这种情况,但是程序中无法保证,(除非

正方形是final)并且继承保证了当父类能用的地方子类也能用,而真要继承

是需要修改正方形内部逻辑,那么客户端调用的时候就无法保证了(难道父类和子类完全不同?)

我觉得描述正方形和长方形有3中解决方案

1,共同实现一个抽象的图形类或图形接口

2,考虑用组合,而不是继承

3,将正方形定义为final,并且保证其内部逻辑的和理性

且这三种方案的代码耦合度是逐步增加的

其实我个人一直反对用计算机完美的描述现实世界的逻辑,毕竟计算机是01的,太精确了

而现实世界不是这么回事,计算机应该是面向问题的,而不是面向逻辑(其实数学建模本

身也是面向问题的)

siberian
2007-03-02 13:19
Coolyu0916

你明显你弄混了,类的内部问题和类与子类的关联问题.

你说30女,60女那是类的内部的.并没有表达到外面来.

而你建立了30女的类,60女的类的时候.那个约束就变成了类本身的重要的东西.

至于用状态模式也好,不用状态模式也好,不过是设计时的技巧,这些约束还是类定义本身至关重要的东西.不能随便的把它外在了.

siberian
2007-03-02 13:29
你说的三种方法,其实在别处的讨论中已经提到.例如阎宏博士哪里就有一个大坑来讨论这问题.

而我今天说到这个问题是,是从类定义的缺乏约束的明确表达这个角度来的.毕竟清晰,无歧义的映射是oo得终极目标,任何一本讲述oo的书都会提到这一点。

我在blog将提到在实际工作中,这会造成那些问题。

Coolyu0916
2007-03-02 13:33
siberian , state模式并不暴露state的

我们使用的统一的方法,给用户也是统一的接口

不过在不同的state下我们引用不同的state类

用户使用的时候肯定是 woman.setAge(30); woman.procreate();

如果不给出age 就采用默认的age,这种接口不对么??

我们平时说女人生孩子,一般来说我们都是认为是年轻的妇女

但是当你告诉他是一个小女孩或者老太太的时候,调用生孩子的方法必然是一个空或者是一个异常(你告诉别人她不能生孩子,或者你跟别人说这是开玩笑)

定义接口的时候你不需要关心这些问题,因为问题只有真的发生才有意义(说女人生孩子没有意义,必须说某个女人生孩子才有意义)

你在考虑问题的时候只要让其具备功能,当你认为某个方法可能存在疑义或者可能出现问题的时候,要在子类或者方法中进行控制。

Coolyu0916
2007-03-02 13:48
checkcode 说实话,我一直到现在还不明白你说的意思

我们举例

如果一个对象 x 继承了正方形 比如说 (x为边大于5的正方形)我们称之为大正方吧

你要对图形计算面积,或者周长

那么你有什么不能定义那??

利用长方形的方法就可以了

子类不使用父类的实现不可以么??

siberian
2007-03-02 14:10
toCoolyu0916

咱们在说类之间的关系.这个时候还处在从需求得到分析类的时刻,而没有到后面设计时运用某种技巧,某种模式的时候.

这个时候,那些职责是他本身的.那些职责是外在的.和state还扯不上关系。

比如说,一个女人类他有哪些行为,那些约束。那么state模式只不过是在确定了这个分析类之后的优化处理,希望把女人类某些东西外包了。

Coolyu0916
2007-03-02 14:51
那么我们就从需求方面走

首先我们确定了人的行为

我们不可能设计一个万能的persion class

我们只要实现有限的功能,在系统中用到的,比如eat() sleep()

现在要在性别上进行区分

这时候我们应当考虑需要怎样区分了,这个区分是为了标识还是为了实现

比如只是为了表示一个身份,一个证件,而不需要一些女人特定的动作,完全就不必需要子类,或者只是为了添加一些附加功能,也是不需要的。

这个必须从逻辑上说的通,你不能说 persion.procreate(),这个不符合逻辑,用你这个接口的人也会晕的,必须是woman.procreate(),这个时候你说不需要继承,使用组合么??或者是简单的引入一个方法么??

至于具体能不能procreate,需求的时候会告诉你,当女人在20-50的时候可能会生育,但是是20还是50是在运行中才会体现的,不是你设计接口时考虑的问题。那是到了详细设计的时候你才要关心的。

siberian
2007-03-02 15:32
类的约束,我们创建一个Person,那么作为一个顶级类,它可以没有约束,作为一个空泛的类而存在,因为它处在抽象的顶级。

但是我们一旦有了一个子类,并且子类的相对于父类有了约束,比如长方形中长和宽可以随便数值,但是到了正方形中不行了。他们要相等,不相等的就不是正方形。这个子类很明显是靠增加约束来建立的,而不是功能的扩展。

我们在需求中挖掘出子类之后,(注意不是为了方便而创造出来的,而是现实中有了子类),就要确定它是否有了约束,还是只是扩展了。

在你举的例子中,如果生育期妇女这个概念常常出现,那么它就应该是一个类,它成立的约束条件就是年龄在20-50,性别女。不满足这个约束,这个类就不成立。它并不是一个运行时的问题,而是一个类本身的,与生俱来的东西。你可以再后来的设计中,把它放到配置文件中,但是不能回避的是,它是类本身的东西。不符合这个约束,他就不是这个类。

接口设计还要排在分析类获取的后面。至于是否方便也不是考虑的问题。我们在这个阶段是要做到完美的映射。

猜你喜欢
3Go 1 2 3 下一页