随笔 - 11  文章 - 33  trackbacks - 0
<2007年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用链接

留言簿(1)

随笔分类

随笔档案

文章档案

搜索

  •  

最新随笔

最新评论

阅读排行榜

评论排行榜

问题是什么
        白马是马吗?本来这不应该是个问题,可是到了OO程序员这里,它就成了问题。不过程序员们通常讨论的是正方形是否是长方形,这和白马是否是马是同一个问题。

问题的起源
       
这就得先说面向对象了,面向对象编程有一个重要原则是里氏替换原则(LSP,Liskov Substitution Principle)。这个原则很简单,就是凡是用父类的地方就可以用子类替换之。形象点就是,老子做得,儿子就做得。可是在程序世界里往往能产生让人困惑的结局。最经典的就是正方形是否应该继承长方形的问题,由于下面将引用别人的讨论,我在这里就不写具体的代码了。父类长方形有长和宽两个属性,有计算面积的方法:长*宽。子类正方形可以继承这些,可是它要保证长=宽,于是它在设置宽或长的时候同时修改长或宽以确保长=宽。现在我把一个正方形的实例a当长方形用,给a设置长是4宽是5,那么计算出来的面积会是多少呢?答案是令人惊讶的25而不是应该的20。这就不符合LSP了。所以,问题产生了,正方形是长方形吗?

个人观点
        正方形是长方形。不过面向对象里的继承关系在现实中并不存在。现实中的父类和子类并不遵循LSP,比如马可以把毛染黑,而白马就不行,因为白马染黑了就不是白马了,这就不符合LSP。实际上,现实生活中的子类是父类的特殊情况(子集),而OO里面子类是对父类的扩展。而且程序里对某个类的定义常常和现实中不一样,比如我们定义的长方形真的能严格对应现实中的长方形吗?也就是说此长方形非彼长方形,那么开头的那个问题其实是人们自己给自己找烦恼。现实中,老子做得儿子不一定做得,但是OO却要求老子做得儿子就一定做得(当然你也可以不遵守)。

下面是转来的别人的讨论,非常精彩:
昨天看了阎宏博士写的<<Java与模式>>一书中讲到里氏代换的内容时,其中提到了长方形是不是正方形父类的问题(第七章 Page 77)我第一次看了他的例子,感觉是那么回事,
函数内容如下:
  public void resize(Rectangle r){
while(r.getHeight()<r.getWidth()){
r.setWidth(r.getWidth()+1);
}
}
书中说道,如果客户端使用一个Square(正方形)对象调用这个函数,就会得出与使用Retangle对象不通的结论。当Rectangle对象时,resize()方法会将宽度不断增加,直到超过长度才停下来,如果传入的是一个Square对象时,这个resize方法就会将正方形的边不断增加下去,直到溢出。
不过我马上就产生了一个疑惑,那个resize函数也太特殊了。
如果我也定义一个函数
其中限制if height = 100 return ;那么高度为100的长方形和长方形类也不符合里氏代换,岂不是也要定义成一个单独的类?而不能作为长方形的子类,依此类推。。。。。我真的蒙了。
  我始终有一个直觉,正方形就是长方形的一种特殊形式,应该定义为其子类,希望高手们能给我一个确切答案。


个人感觉正方形的确不是长方形的子类,它是在长方形上多了一个条件“长 == 宽”。所以,长方形适合的地方,正方形不一定适合。
如果一定要把长方形和正方形拉上关系,我认为正方形应该是通过长方形来实现的。只是在正方形的setter方法里面加上一定的控制,让它里面的长方形的长和宽相同。
呵呵,昨天才看到这一章,这是我的理解,不知道斑竹大人和各位高手怎么看^_^


to binny(骑个破车看夕阳) “个人感觉正方形的确不是长方形的子类,它是在长方形上多了一个条件“长 == 宽”。所以,长方形适合的地方,正方形不一定适合。”
  没错啊,正方形只不过是长方形的一种,长方形->长宽比例固定的长方形->长宽比例固定而且长=宽的长方形,,这样不行吗?
  或者就是在resize中将有限的特殊情况考虑进去,不然,把什么都特殊化,那也就失去特殊的意义了。


to binny(骑个破车看夕阳) “个人感觉正方形的确不是长方形的子类,它是在长方形上多了一个条件“长 == 宽”。所以,长方形适合的地方,正方形不一定适合。”
如果我也定义一个函数
  其中限制if height = 100 return ;那么高度为100的长方形也成了一个特例了,其他长方形都适合,“这种特殊长方形”就不适合了,怎么办?if height =200,=300.....5000呢?又怎么办?


说得有道理,其实,后面写到的的四边形与长方形的关系,包括马与黑马的关系,都不是继承关系(虽然书上写的是继承)。
很显然,如果我让四边形的一个内角增大到比另外一个内角要大,无论长方形和正方形都不符合。
同样,马科已被染上各种颜色,但是黑马不可以,她如果被染上了除了黑色以外的颜色,它就不是黑马了。
实际上,oo中的继承指的是功能上的继承,而不是条件上的继承。也就是说,子类应该有父类所拥有的功能,并可能有所发展,而不是在条件上面有所发展。这样才能符合理氏代换,否则,条件都增加了,怎么能替换呢,这样只能是子集,而不是子类。


do not take it toooooo serious


一点看法:
组合关系是客观存在的,继承关系并非客观存在的,推荐尽量少用继承。


很抱歉,书中有一处打印错误
  public void resize(Rectangle r){
while(r.getHeight()<r.getWidth()){
r.setWidth(r.getWidth()+1);
}
}
应当是
  public void resize(Rectangle r){
while(r.getWidth()<r.getHeight()){
r.setWidth(r.getWidth()+1);
}
}
由于篇幅限制,在我的书中并没有引入Design by Contract的概念。子类应当完全继承父类的contract。
上面的检测条件在Rectangle类的contract里面,但是不在Square类的contract里面。这就是错误的。
反过来,楼上的朋友所提到的width == 100这个条件并不在Rectangle的contract里面,因此Square不必继承它,当然也就谈不上违反。
如果你一定要把width == 100这个条件放到里面Rectangle的contract里面,那么Rectangle就变成了(比如)边长不等于100的长方形,而子类是一个边长等于100的长方形。那么当然子类违反了LSP。这个时候你就只好把边长等于100的长方形和边长不等于100的长方形并列起来,并为它们选择一个共同的子类。


并为它们选择一个共同的父类。
(还没睡醒)


OOSE书中均有解释,继承分成多种,具体我不太记得清,大概10种左右吧,每一种有其适用范围,这种属于父类输入条件对子类不适用的一类,当然可以这么做,但是会有各位提到的缺陷,真正适合继承的种类非常少,所以说少用继承。


长方形有二个属性长和宽。并有一个设置长的方法和设置宽的方法,还有一个求面积的方法.
像下面
private int length;
private int width;
public void setLength(int lenght) {
this.length = lenght;
}
public void setWidth(int width) {
this.width= width;
}
public int getArea() {
return this.length * this.width;
}
如果说正方形是长方形的子类。为了保证正方形长和宽相等,那对应于正方形的二设置长宽的个方法就得改成
public void setLength(int lenght) {
this.length = lenght;
this.width= lenght;
}
public void setWidth(int width) {
this.length = width;
this.width= width;
}
那我们想想用户使用时候的情景。 我们都知道长方形的面积等于长与宽的积。那当我们用长方形的时候我们会这样用
Rectangle rectangle = new Rectangle();
rectangle.setLength(5);
rectangle.setWidth(4);
我们想知道面积是多少我们就可以
rectangle.getArea();
得到的是20,当然结果是非常正确的。
但想想如果我们把一个正方形的实例给用户用的时候会怎么样
Rectangle rectangle = new Square(); //注意,这里体显代换原则。用户根本不知道真正的实例是正方形,用户只知道长方形的事情。
rectangle.setLength(5);
rectangle.setWidth(4);
我们想知道面积是多少我们就可以
rectangle.getArea();
得到的结果却是 16 ,这违背了长方形的面积是长与宽之积的原则。用户就不会明白为什么我设置了长是5宽是4得到的答案却是16 ?? 与前提不符
所以正方形不能代替长方形出现在这个地方。
也就是说正方形不应当看作是长方形的子类。


to:jeffyan77(jeffyan77) 是阎博士本人?首先,非常感谢能够得到你亲自指点。你的书文笔很好,实例多多,由浅入深,我去年就翻了一下,印象很深刻,不过当时没有做JAVA,所以没有仔细研究,现在正好要用JAVA做项目,于是下狠心买了一本您的书,打算慢慢学习。希望能有更多想你这样的书出现在市面上。
  就这个话题,您提到“Design by Contract”又是什么理论?在哪里可以找到相关资料?如果内容不多,能否在这里多说几句?
  顺便说一下,飞思网站最近无法注册,不能进入你的专栏发贴,所以我跑到这里来了,没想到还真碰上了你。


据我所知孟岩好像有一本译作是关于DbC(Design by Contract)的,马上就要出版了。建议到www.china-pub.com看看。
所谓Contract,就是指一个方法的前条件、后条件加上不变量。所有的继承都应当是Contract的继承。一个简单的介绍可见
http://www.gauss.muc.de/tools/dbc/dbc-intro.html
Eiffel语言直接支持DbC。如果你使用Java的话,可以考虑安装iContract,那样可以在Visual Age for Java里面直接声明Contract。
我的意见,如果仅是应用的话,理解DbC的含义就可以了,除非对DbC理论有特别兴趣,不然不必要读专著。
飞思网站的问题我会和网管讲,谢谢提醒。


truezerg(赵明宇) ,
1.你这个例子的确能说明一定问题,不过这并不是我的疑惑产生的根源。
概括来说,我的主要疑惑在于对特殊化的程度。从你这个例子看来,长宽比例固定的长方形此时的地位就和正方形相似了。其实每一个长宽不同的长方形,就细了说,都可以说是特殊的,只不过我们常常用到正方形这种特殊形状。那么,就这些“相对有限”的特殊形状,能不能在长方形的相应属性中设置标志,比如叫boolean whFixed 构造器中是whFixed = false .在setWidth和setHeight中就判断这个值的真假做相应的处理。Square类可以有一个构造器,内容是whFixed = true;
2.你所说的。。。。。“但想想如果我们把一个正方形的实例给用户用的时候会怎么样
Rectangle rectangle = new Square(); //注意,这里体显代换原则。用户根本不知道真正的实例是正方形,用户只知道长方形的事情”。。。。。。。。。。。  
  结果面积得出的是16而不是20。我认为这是很正常的事情,那只能说用户对正方形的特性还不完全了解。作为子类,肯定有和父类不同的地方。包括方法的重写等,也是常用的。另外,我记得有个著名的以以Shape和其他形状的calcArea来说明多态的例子:许多不明形状的对象放在一个数组中,然后用(Shape) arrShape[i].calcArea,能够得到相应对象正确的面积。也就是说,你的那个例子,本来就应该这样。


结果面积得出的是16而不是20。我认为这是很正常的事情,那只能说用户对正方形的特性还不完全了解。作为子类,肯定有和父类不同的地方。包括方法的重写等,也是常用的。
------------------------------------------------------------------------------
你所说的用户对正方形的特性还不过完全了解。这句话不对。 首先如果把正方形当成长正形的子类的话,用户没有义务去了解正方形。因为它们是拿它当长方形来用的。你不可能让用户去了解所有的子类。如果每个子类用户都必需要了解它们的特性,必需要撑握如何使用它,那就失去了多态性了。因为每当使用一个新的子类去换掉父类就得对这个子类的特别情况加以处理,这非常不合理。
方法重写的问题:方法可以重写,但要符合父类限定的条件。在这里,长方形求面积的方法前提规定的是长与宽的积。如果你重写了这个方法但没有符合这个前提,程序倒是可以编译通过,但不符合代替原则。所以出现这种情况有种暗示你的类结构有点问题。(当然如果你改变了前提条件的话或许有可能,比如改变长方形求面积的规则)
Shape类的问题:
对于长方形继承Shape类的这种情况,和正方形继承长方形有所不同。 因为Shape类没有一个求面积的方法(没有实现)。所以就没有一个前提规定该如何求。所以长方形当然可以自己实现如何求。没有任何限制。但正方形继承长方形以后就受到长方形的限制了。
另外,如果我们找到了Shape类的统一的求面积的方法。比如用微积分来求。那长方形继承Shape也是正确的。 因为用微积分的方法来得到长方形的面积,得到的也是一个正确的结果。长方形并没有违反父类的规则,同时任何图形都可以通过微积分来求得面积,而且都是正确的,所以用户根本不用考虑给我的是什么图形,根本不用考虑这个图形的特殊点。只要通过父类的求面积的方法就可以得到正确的结果。


我觉得无论正方型作为长方形的子类或者长方形作为正方形的子类都有不妥的地方。它们有很多共同的地方也有很多不同的地方。可以设计一个抽象的“矩形”类,长方形和正方型都继承它。所有长方形和正方形的共同特性在父类“矩形类”中实现,所有不同的特性在子类中实现。这样不会影响长方形和正方型的内部实现逻辑,而且可以保证最大程度的功能复用。


我觉得还是把长方形看成是类,正方形看成是长方形类的一个实例比较合适。


阎博士你好,没想到能遇见你,很高兴,同时想请教关于您书上的一个问题。和上面的兄弟一样,我也觉得黑马并非是马的一个子类。当然,黑马“是”马没有错误,我只是觉得所谓“黑”只是马的一个属性,所有马都有颜色这个属性。黑或白只是属性的值不同。 不知道为什么黑马和白马要成为马的子类。这样做有什么有什么实践价值?
麻烦了阎博士,请您再说明白一下。谢


所有的继承关系都可以改为委派关系,所有的is-a关系都可以改为has-a关系。
譬如在某些系统中可以认为Person是超类型,Man和Woman是子类型,这是继承关系,这个提法在很多著作中都当作例子出现,比如Peter Coad的书。你当然也可以把性别拿出来,变成Person的属性,这样就成了has-a关系。(这种做法是State模式或者Strategy模式。)
你可以把工作中的继承关系拿出来,按照这个思路试一试。
OO技术是一个强大的工具,几乎每一个问题都可以由多个解决方案。


PeterChen所说的“可以设计一个抽象的“矩形”类,长方形和正方型都继承它”最接近我的意见。
大家讨论。


我觉得正方形完全没有必要当做一个类来出现。它就是长方形的一个实例,只不过它是一个长和宽相等长方形。
当然设计一个抽象的矩形类,然后长方形和正方形都继承它也没错。
什么样的设计取决于什么样的使用环境。 设计和使用环境是分不开的


我觉得问题在于继承一个具体类上。
Square继承的是Rectangle这个具体类,而这个具体类又实现了Area这个方法。那么子类的Area要符合父类的限定条件。
而如果设计一个抽象的矩形类,则没有对Area方法有具体的实现,所以子类可以自己实现。
假设我们设计一个具体的四边形的类,定义了改变角度的方法,那么长方形是不是也不应当去继承?


to:truezerg(赵明宇)
...........................
我觉得正方形完全没有必要当做一个类来出现。它就是长方形的一个实例,只不过它是一个长和宽相等长方形。
当然设计一个抽象的矩形类,然后长方形和正方形都继承它也没错。
什么样的设计取决于什么样的使用环境。 设计和使用环境是分不开的
...........................
这几句话,我基本赞同


看了阎博士的解释豁然开朗,呵呵,受教了。
楼上的,我想你的疑惑和我前天的想法是一样的
但是现在,我不会在一个四边形的类里面写改变角度的方法,也不会写改变边长比例的方法,
因为不是所有的四边形都可以有这两种改变,
所以抽象出来的四边形也不应该有这两种改变。
如果业务要求作这两种改变的话,
那么它操作的对象应该不是四边形,而是除了长方形以外的四边形。
TO:truezerg(赵明宇)
感觉这个问题上我们的理解有许多共同的地方
而且在许多贴子里面看到你的发言,感觉你对java的理解非常透彻
把你的MSN给我好吗,想和你好好学习一下,呵呵


设计是要看目标的,如果我们设计的正方形和长方形在真正的企业级应用中那就要考虑扩展性和稳定性等因素了。
正方形现在的和长方形唯一不同的是width==height,但是在真实环境中要考虑将来正方型和长方形都有可能发生变化,它们有可能有更多不一样的地方。如果变化了我们的设计是否需要改动?如果是这样我还是觉得“设计一个抽象的矩形类,然后长方形和正方形都继承它”的方法好一些。因为这种方法可以有更好的可扩展性和灵活性。
而如果仅从单纯的教学例子的角度出发我还是比较赞同“正方形是长方形的一个实例”的。
所以正如truezerg(赵明宇) 所说:“什么样的设计取决于什么样的使用环境”


breakpoint_fish@hotmail.com
我的这个MSN里面基本上全都是程序员。都是讨论问题的,互相学习而已。


不好为什么msn上不去了,郁闷


长方形的学名应该是矩形,矩形的定义(对边相等,四个角为直角的四边形或有一个直角的平行四边形)中并没有“长不等于宽”,从这个定义来说,我觉得似乎没有理由认为正方形不是长方形的子类;另外我觉得resize()的定义也有问题,这只是普通矩形即非正方形的行为;子类也可以重载父类的行为(方法),否则重载又有何意义?
如果说上面的例子中所指的长方形是普通长方形(即长宽不等的),那么这个问题就没有讨论的必要了,在现实中它们也不是父类和子类的关系。
可见这个例子实在不是个很好的例子。


是的,上面的“矩形”说法不够严谨。实际上我记得我的书中说的是抽象“四边形(Quadrangle)”类,长方形Rectangle类和正方型Square类都继承它。
长方形的Contract是width和height可以独立变化,这个contract在正方形中被破坏了。为什么说长方形的Contract包含了独立变化这一条呢?你看看长方形的接口就会发现,width和height可以独立设定,这就是说二者可以独立变化。而在给出正方形时,即便接口不变,也要千方百计地破坏这个contract,因为正方形不可能有两个边长。
所以从Contract继承的角度上讲,Rectangle和Square不可能有继承关系,它们合并起来,可以给出一个更大的Contract,这就是一个抽象超类。
这个长方形和正方形的例子不是我发明的,这是至少流行了十年的思辨题目,最早来自于C++和Smalltalk领域。我把它搬到了Java领域,问题和答案都没有改变。
这个问题之所以有意义,就是因为它说明继承的概念与日常生活中的概念或者数学概念都是有区别的,在数学上长方形当然包括正方形,但是在OO设计中除非你将二者设计成Contract继承关系,不然它们就不是继承关系。
当我们说到Rectangle是不是Square的超类时,我们所说的不是数学上的Rectangle和Square,我们说的是一个定义好的Rectangle类,和一个不知道如何定义的Square子类。结果我们发现这个Square最好独立定义,而不是作为Rectangle的子类定义。
大家讨论。


看了敏捷软件开发,里面提到这个问题说:
DBC对Rectangle的SetWidth这个方法的前置条件是this.Width == newWidth and this.Height == oldHeight,很明显Square的SetWidth不满足Rectangle的Contract,所以不应当设计成继承关系


>>长方形的Contract是width和height可以独立变化,这个contract在正方形中被破坏了。
我觉得这有偷换概念的嫌疑,width和height可以独立变化本身就是长方形中的某些“特例”(虽然从比例上说几乎是全部长方形),并不能用作长方形的Contract。如果可以,那么以此类推,四边形的四角大小也是可以变化的,只要满足360度总和的条件,但长方形显然四个角大小是不能变化的,由此岂非可以得出类似结论:长方形也不能作为四边形的子类?


to tripofdream(梦之旅)
Square违反Rectangle的Contract,是站在SetWidth,SetHeight这个角度来看的。
如果你从改变四边形角度的方法看,那长方形与四边形也应该不能使用继承关系。但如果从其他角度看,依然是继承关系


要清楚子类是对父类的扩展,而不是对父类的“约束”,所以如果四边形里定义的方法或性质无法让子类(比如长方形)来扩展,反而却因为子类的存在约束了父类的行为。那就是错了。
所以长方形能不能做四边形的子类关键看四边形的定义。
还是那句话,设计取决于你的应用环境


有种真理越辩越明的感觉
再来谈谈我的想法
首先,继承关系是人自己想象出来的,并不客观存在。是人们用来实现复用和多态的一个手段。
所以脱离代码讨论是否是继承关系是空洞的。
其次,既然是用来实现复用和多态的,那么,想定义某两个事物之间的关系是继承关系,
要看他们是不是有利于复用和多态,而不是在我们定义中符合什么什么关系
总之,我们不是为了继承而继承。所以试着不要直接就认为他们是继承关系,
然后找他们不是继承的原因。反过来试试是找理由说服自己它为什么是继承关系。


tripofdream(梦之旅) :
width和height可独立变化不是长方形的特例,这是存在于Rectangle类代码中的。注意我们讨论的并不是数学的长方形,而是叫做Rectangle的一个类。
只要width和height失去独立变化的能力,不管它们是相等还是形成一个比例,都不能成为Rectangle的子类。
譬如width = N * height的条件一旦加到类上,那么这个类就不再是Rectangle的子类。N=1的时候就是我们所讨论的正方形。
说的更加广泛一点,只要width和height之间出现约束,譬如width = f( height ),那就不再是Rectangle的子类了。


正方型应该是矩形的子类,类的关系不能以太生活化的逻辑评定。最抽象最有共性的当然应该是父类,逐级分类。


楼上的,那是抽象与个体的关系,是类与实例的关系。
没人说过“最抽象最有共性的当然应该是父类”这样的话呀
是不是继承关系,要看你的具体设计是什么样的,
其实长方形正方形可以设计成继承关系,也可以不是,
因为继承是语法上的东西,你写上extends他就是继承
但是设计成继承关系是不是合适,那就是另一回事了。
我们现在讨论的就是把他们设计成继承关系是不是合适的问题。


对!楼上说的好,设计模式就是让我们不要乱继承~
例如:植物类有生长方法,一块砖头为了生长不应继承植物类,虽然你可以extends也没错!
但不和理。


晕,能有这么解释的吗?
到底正方形是不是长方形的子类取决于你抽象的程度和抽象的语义。
如果你定义“长方形”(Rectangle)这一类对象的固有特征是有两条互相垂直的边( Rectangle.width ,Rectangle.length),能够计算出面积(Rectangle.getArea()),
定义菱形为(Diamond)拥有四个相等的边(Diamond.side)的实体,能够计算出面积(Diamond.getArea())
毫无疑问,这在c++里面不是问题,因为它支持多重继承,Square :Rangle,Diamond.
但是java不支持,于是这就成了一个问题,到底哪个是父类,哪个该作为一个接口实现?
但是确确实实,Square是一个子类,而不是Rangle和Diamond的聚合。
上面有人提到了当 Square.setWidth(5),Square.setHeight(4)以后,为什么最后getArea()==16而不是20,这和父类没有关系,也不需要你去理解父类,而是你的特殊子类修改了父类的实现细节。当你的Square.setHeight(4)的时候,你应该以某种方式(比如异常)告诉调用者,他的行为已经隐含的修改了Square.width——你的setHeight()已经修改了父类的方法!!!
抽象不是空中楼阁,不是哲学上讨论白马非马的问题,而是为了让你的设计更清晰的体现现实生活中的对象实体,如果没有现实条件的依托,你的抽象还有什么意味?(说句不中听的话,与其看一些夸夸其谈的东西,不如回头看看哲学)
在一本书看到的一个观点是:如果你的抽象继承的层次超过了5层,你应该回头检查一下你的设计了。这个原则适合于任何设计。


我强烈赞同 asdmonster(asd)的言论!这才是我要的结论!
当然,这是我的看法,本贴再留一留,大家也讨论了这么些天,估计不同意见也不会太多了。


当你的Square.setHeight(4)的时候,你应该以某种方式(比如异常)告诉调用者,他的行为已经隐含的修改了Square.width——你的setHeight()已经修改了父类的方法!!!
-----------------------------------------------------------------
姑且不谈用异常的方式告诉调用者在设置长的时候也设置了宽这件事情好不好。 从没有见过这样用异常的。
但是不管你要不要告诉调用者长与宽同时被改变了,这都是违反了长方形(也就是父类)的规则。用户就不明白为什么我设置一个长方形(因为用户不知道实例是个正方形)的长或宽总是发生一个异常?? 这比什么也不告诉用户而是得到面积为16更加不可思议。


TO:asdmonster(asd)
>当你的Square.setHeight(4)的时候,你应该以某种方式(比如异常)告诉调用者,他的行为已经隐>含的修改了Square.width——你的setHeight()已经修改了父类的方法!!!
这种说法的确有些不合适。
第一点,在Rectangle的setHeight方法中如果有异常抛出,那么Square的setHeight方法语法上是可以抛出Exception的,但是如果Rectangle的setHeight方法中没有抛出异常呢,Square的setHeight是不能抛的,怎么办,如果正好Rectangle的setHeight没有返回值呢?还要修改父类吗?
当然,这只是个极端的例子,我的意思还是那句话,脱离了代码,脱离了设计,空谈是不是继承关系是毫无疑义的。
第二点,正如 truezerg(赵明宇) 所说,用户每次给他的长方形设置长的时候总是发生异常,或者说告诉他“他的行为已经隐含的修改了Square.width”,用户岂不是很冤枉,“我只是想调一下setHeight()”。然后你只能再告诉用户“你现在调用的是正方形,不是长方形”,那用户会很生气的“我想要只鸭子,两条腿都能动!你却给我个两条腿长在一起的,我想让它迈腿它就乱叫……我只是想要个两条腿走路的鸭子!”
也许比喻得不恰当,但是实际情况就是这样。
另外,TO fbysss(独孤求败)
这么好的帖子,留着吧,大家在这个帖子里面获得的知识比能得到的专家分要多得多。
还是希望大家能在这里继续讨论。


mark


我觉得还是有必要再说说我的观点:脱离了代码,脱离了设计,空谈是不是继承关系是毫无疑义的。不同的代码,他们的关系可能是不同的。
是不是继承关系是人定的,而不是客观世界,因为它本来就不是客观的。继承的合适不合适才是我们讨论的问题。
《java与模式》中说到的长方形不是正方形的父类,是针对书中的那几段代码而说的,而不是针对我们在小学数学里面学到那个长方形与正方形。书中的那几段代码,正方形的确不应该继承长方形,原因前面已经讨论很多了。
不同的代码描述客观世界的事物时关系可能不同。
我们可以用别的方法来写长方形
public class Rectangle {
public int getSideCount() {
return 4;
}
public int getInteriorAngleDegree() {
return 90;
}
}
这样写,正方形完全可以继承长方形。这是因为在这个长方形里面没有涉及到width和height是否可独立变化这个Contract。当然这样的长(正)方形也不能存取他们的长和宽,也不能计算它们的面积。如果你的设计要求就是这些,你完全可以设计成继承关系。反之,只要涉及到存取他们的长和宽,就不能继承。
脱离了代码,脱离了设计,空谈是不是继承关系是毫无疑义的。
书中的长方形不是正方形的父类,是针对书中的那几段代码而说的.


总算有人说了明白话!


好,我留着,欢迎继续讨论!有的帖子还没时间看真切,得慢慢研究
“到底正方形是不是长方形的子类取决于你抽象的程度和抽象的语义。”
我赞同 asdmonster(asd) 的主要是这句话。


是不是子类可以扩充父类,而不可以通过限制来特殊化
那白马黑马呢?这么说黑马不是马的子类


to阎博士:论坛不能注册,所以在这里说两句。这本书刚看完,受益匪浅,不过要做到灵活使用,看来还要很长一段时间。冒昧的问一下,博士从初学java到现在如此透彻的程度花了多少时间?


是不是子类可以扩充父类,而不可以通过限制来特殊化
那白马黑马呢?这么说黑马不是马的子类
------------------------------
可以特殊化,但不可以突破父类的规则。
我们都一再强调了,在讨论什么是不是什么的子类的时候一定要有前提条件。一定要有特定的环境。如果单纯说白马黑马是不是马的子类的话。那说是也对,说不是也对。 因为大家没有站在同一个环境上说话。


OO是个好东西,但是并非万能。
c++虽然不是最早的OO语言,但是却是早期OO语言中影响最大的。c++发展OO将近20年,没有任何显著成果。直到90年代末加入了泛型设计,用STL(标准模版库)重新构建了c++库后,c++才真正成为了一个出色的语言。
java的OO能力比c++出色,这是好事。从JDK1.0开始至今,java基于OO思想架构的类库日趋完善。但是过分的OO也很容易将设计引入死胡同。产生“白马非马”、“正方形非长方形”的java类,就是这种陷入误区的典型例子。
如果你发现自己的设计局限性很大,无法适应特例,那么你重新考虑架构设计的时候来了。


呵呵,别生气。
Square已经更改了Rectangle的语义
——
我相信大家都记得这么一个东东:Deprecated.
上面有人说过design by contract .这就是一个破坏契约的例子。因为Rectangle缺省的语义是setWidth()不改变height,但是它改变了。设置Square比较合适的方式是用从Diamond继承下来的setSide()。
刚开始我写到对Square.setWidth()的时候面临这两种选择,要么是使用Deprecated,要么就是抛出异常。后者可能严厉了点,但是我实在想不出别的法子让Square屏蔽掉Rectangle的setWidth()和setHeight()方法。
这就产生了一个问题:Square从Rectangle继承了什么?我个人的理解是它继承的是有两条垂直的边,仅此而已。


楼上的,既然那么为难干吗还要继承呀


java推荐使用集合而不是继承。


看样子,大家老是模式,模式的,
大家忘了有一个东西叫:原则。模式是因原则而生的。
OCP原则:老爸能去的地方,儿子一定能去。
可正方形,长方形中:
  public void resize(Rectangle r){
while(r.getHeight()<r.getWidth()){
r.setWidth(r.getWidth()+1);
}
}
长方形能作的事,正方形不能作,违反了OCP原则。


为什么大家一定要强调长方形的长大于宽呢?
如果一定要强调这一点,那么正方形的长等于宽就不符合了,正方形不是长方形(即正方形不是长方形的子类)
但是,初中的几何课本上就不再强调这一点了,长方形(矩形)是特殊的平行四边形,有一个脚为直角,并没有规定那条边长,那条边短,面积等与相邻两条边的乘积
同时也规定了相邻两边长度相等的矩形是正方形
按照这个定义:正方形就是长方形的子类


单纯争论长方形与正方形的关系没有意义。 不如谁举一个现实中的情况大家来讨论还差不多。 要不楼主考虑结贴吧。


如《Java与模式》书中提到的java.util.Properties和Hashtable就是一个好的例子。


binny说的很好。
父子关系是根据所支持的操作的。仅仅根据名字来说谁是谁的父亲没有意义。
数学中为什么Square是Rectangle呢?因为数学中不存在setWidth, setHeight这些过程性的变化。数学中的函数和c++/java中的函数是不同的概念。数学函数没有副作用,而java/c++中的函数其实应该叫做过程/方法更合适。
数学中,Square(5)是一个边长为5的正方形。它也是一个长和宽都是5的矩形。
而当我说“把一个边长为5的正方形的一边设为3"时,那其实是意味着一个新的矩形,Rect(3,5)。
如果我们变换Square和Rect的支持的方法,父子关系就可能随之变化。
举个例子:
一个正方形可以当作一个只读的矩形来用。
一个矩形可以当作一个只写的正方形来用。
RectR{
int getHeight();
int getWidth();
}
RectW{
void setHeight(int i);
void setWidth(int i);
}
SquareR{
int getHeight();
}
SquareW{
void setHeight(int i);
}
Square:SquareR, SquareW{}
Rect:RectR, RectW{}
那么,我们有Rect可以是一个SquareW, Square可以是一个RectR。
而Square和Rect之间却没有直接的联系。
另一个例子,如果我们把setHeight(),setWidth()做成functional的,也就是模拟数学的方式生成一个新的矩形,(个人比较喜欢这种immutable的设计)那么,Square就可以理直气壮地当成Rect用了。
Rect{
int getHeight();
int getWidth();
Rect setHeight(int h){return new Rect(width, h);}
Rect setWidth(int w){return new Rect(w, height);}
private final int height;
private final int width;
}
Square extends Rect{
int getHeight();
}
总结:
为什么正方形不是矩形?
因为你设计的这个“正方形”,“矩形”不等同于数学意义上的“正方形”,“矩形”。OO设计中的父子关系要根据支持的操作。


看了很久了, 越看越明白


看了看,想了想,违反现实的设计就是错误的。不管辩解者有如何的公孙龙。
黑马它就是马的子类,瘸了条腿的黑马也是黑马的子类。父类能去的地方,子类就能去。这种替换本身就有问题。像这种和现实不符的问题说明了oo的局限。
我赞同asdmaster的说法,我觉得是那是一个有意义地修正。向上转型未必完全安全。
要保证向上转型安全,那么就要在程序的抽象世界中强迫保证子类不违反父类的行为(专制呀)。也就出现了白马非马的问题。
真的希望有那位大家可以重新提出一个思想来让程序更加贴近现实,也更方便。现在面向对象思想达到它的初衷(在计算机世界中再现现实)了吗?我觉得没有。


面向对象不是要在计算机世界中再现现实


yes! 想用OO来在计算机中模拟现实中的一切是非常可笑的


花了40分钟,看完这个帖子,受益非浅。


长方形和正方形,这两个,怎么说都有道理。
世界事相对的世界、不管事什么方面,都有一个局限性,这就是oo的局限性。
如果你再实际的开发中,非要分出这两个关系,恐怕你会走入深渊!
你说长方形不是正方形的父类,而我就可以。
马和黑马的关系,在通常的意义上并不是长方形和正方形的关系。
为什么??
因为对于马来说,应该有一个属性,叫做颜色。而当这个颜色为黑的时候,那么马就是黑马。
而对于长方形和正方形来说,很少有人会给他一个属性,标志他的这个属性吧?
所以我说,我可以把正方形当作长方形的子类。我可以给rectangle一个属性,让他去标示到底是长方形,还是正方形。


很遗憾,我们拥有的就只是现实。并没有一个供你幻想的黑客世界。我觉得oo里面就是没有显式地表达条件,这个现实中很重要的东西,才有这样的尴尬。
其它类型的语言也是同样的思路。像大家熟知地人工智能语言。很可惜,只说明了一个问题。人类对现实的了解远不如对机器的了解。
我有一个思路是去定义一个final验证方法来模拟类条件,并加到构造方法中去。凡是符合这个条件的就是子类。这样不能解决大部分问题,但是应该比一点条件限制也没有好,至少在程序的抽象世界里会比较有效。
象正方形是否是长方形地问题。只需符合这个长方形地条件就可以了。有的方法失效是在现实中是正常现象。多态的问题,只好先验证一下,子类也做点提示告诉用户。


其实,说实话,这个问题挺无聊的


首先是我的题外话。OO(面向对象,我认为从一个C++开发者的角度严格地说,我们在讨论基于对象OB,当然java里面已经也有OB没错但那不是地道的java,另一个话题了)是一种方法。它是一个动词,而不是一个名词。it's a verb, not a noun.动词无所谓对错。“吃饭”,这个动宾结构的动词短语有对错吗?“骑车”,有对错吗?没有。所以说“OO里面的尴尬”我觉得不太成立,“吃饭”的尴尬?吃饭能否产生尴尬,能。“西方人用筷子吃饭”确实会尴尬,但那不是吃饭的尴尬。一句话,看是谁在吃,看是怎么吃而已。
其次是题外话之二。由吃饭的例子引发开去的。吃饭是人类取得能量的一种方法,其在人类获得能量的诸多方法中的地位,和今天的面向对象在软件开发中的地位颇为类似。东方人喜欢用筷子吃饭,西方人喜欢用刀叉,这是文化的差异。但是有一点是一样的,它们都是吃饭。
无论在东方人的意识中,还是在西方人的意识中,吃饭,都是表示一种涵义,就是那个含义,呵呵,你知道的,不用说,也没法说清楚,保证不会理解错。
其三,回到我们的正题。但是离不开上面两点。一般来说,吃饭要喝汤吧,就像OO要继承一样。问题出在这里,重点来了。喝汤的概念,东西方人都一样,可是继承的概念,在东西方人的脑海中,以我对东西方人的观察来说,还是有那么一点略微的区别。inherit,继承。我们先看看《简明英汉词典》如何解释inherit:
inherit
[ in'herit ]
vt.继承, 遗传而得
显然,如果词典没有出什么大的差错的话,在西方人的思维里,继承是“遗传而得”。什么叫遗传而得,从生物学的单亲繁殖的角度来讲(java就是单亲繁殖),除非产生变异,否则子个体至少在DNA上,和母体相当。java里面用了extends这个词,愚以为一语中的,extend在英文里有“扩充, 延伸, 伸展, 扩大”的意思。回到长方形和正方形的问题上,认为正方形是长方形子类的同学可以考察一下正方形这个类的DNA(开玩笑),正方形extends了长方形没有,显然没有。正方形is-a长方形,没错,难道不应该继承吗。注意,我们在讨论类,没有讨论实例。java教科书中对类的定义大致为:<b>类是定义同一类所有对象的变量和方法的蓝图或原型。</b>
正方形类的实例,确实is-a长方形类的实例,为什么?因为长方形类extends正方形类!
长方形类是扩展的正方形类。用集合论中的韦恩图来表达一下会更清晰。长方形是一个大类,而正方形是长方形大类中的一个小类。用通俗的话讲,长方形类比正方形类来的大。
它继承了正方形类的DNA。
现在看看class Square : extends Rectangle 是多么愚蠢!
明明应该是 class Rectangle : extends Square
第四,现在你看OO是多么悲哀,"面向对象不是要在计算机世界中再现现实"。因为也许你会发现以下状况:
class Rectangle : extends Square 的写法,估计会得罪了工程人员,这样符合“道义”的写法,在工程上完全没有意义(没有哪个工程不是超类先行的)。就像用某个有限状态自动机的状态转换图来构造一个词法编译器一样对人类来说是不现实的(或许《黑客帝国III》中那个白头发白胡子的设计师老爷爷可以完成他,理由很简单,他是机器(程序),而我们是人。)
class Square : extends Rectangle 的写法,估计会得罪了学院派。这明明是道义的背叛。而且会产生这个帖子所讨论的种种问题。原因很简单,你要继承一个父类,你得保证你的子类能做父类能做的事情。在C++中你实现一个读写汉字的fstream文件IO流,一次读写两个字节的实现是错误的。因为你这个流至少也要能搞定英文,否则,就不应该用继承,而应该用复合。
第五点,谈谈如今在中国的计算机著作翻译界,都存在很多缺乏足够功力和底蕴的译者。很多译著中都没有区分“类”和“类的实例”,把“继承”和“扩展”搞得如此云里雾里的现象,译者的责任也很大。诸如此类的现象还有很多。
欢迎和我交流 b00251420@sei.ecnu.edu.cn


楼上说:很多译著中都没有区分“类”和“类的实例”
真的呀?难以置信...
class vs. instance of class不加区分?还是
class vs. object不加区分?
恐怕是英文原版书就没有区别,照我看,这不过就是软件业专业术语不够规范的一个例子。
举个例子,JavaScript里面根本就没有类的概念,可以很多书中还要正经八百地谈论“类”,这就是属于不规范,这种现象是普遍存在的,不一定是翻译的责任。


尊敬的jeffyan77,你好。
我知道原版书就是没有区分的。因为我们用的就是原版书。你说的是对的,这不过就是软件业专业术语不够规范的一个例子。看来上至博士,下至我们这种半文盲,都对此有所感触。这是我本来的意思。但是我不想刺激国外的作者,因为他们是我的偶像,我知道他们是写得不够规范,但为什么译者们不为自己的偶像做一点有益的修改和补充。其实他们分得清" class " 和 " instance of class"。
JavaScript我不懂,也没有看过这方面的书。我对博士的渊博表示钦佩。
最后,非常喜欢你的研究。
另外向CSDN表示歉意,最近C++和Java两头看,所以写出class Square : extends Rectangle这种句子,反正只要明白意思就行了。


public class Square {

public Square (){
oriRec = new Rectangle();
}

public Square( int r){
oriRec = new Rectangle(r,r);

}

public void setRim( int newRim){
oriRec.resize(newRim, newRim);
}

public int getRim(){
return oriRec.getWidth();
}

public void resize( int newRim){
oriRec.resize(newRim, newRim);
}

public int getArea(){
return oriRec.getArea();
}

private Rectangle oriRec; //originalRectangle;

};
public class Rectangle{
public Rectangle (int x, int y){
height = x;
width = y;
}

public Rectangle (){
height = 0;
width = 0;
}

public void setHeight(int x){
height = x;
}

public int getHeight(){
return height;
}

public void setWidth(int y){
width = y;
}

public int getWidth(){
return width;
}

public void resize(int x, int y){
height = x;
width = y;
}

public int getArea(){
return width * height;
}


private int width;
private int height;
};






最后说明一点,学好C++。
jvm 连 两个分号 ;; 都编译不过。不知道代码优化部分是怎么写的。


TO: yangye1211(杨杨)
有深度
另外,我也觉得目前Java的术语五花八门,通常一个单词有很多中文叫法,不同的书不同的写法,
真的该统一一下了


--------------------------------------
正方形类的实例,确实is-a长方形类的实例,为什么?因为长方形类extends正方形类!
--------------------------------------
先不问到底该不该继承,上面这句话怎么说都是错的。
如果:因为长方形类extends正方形类
所以:正方形类的实例,确实is-a长方形类的实例
那这句话正好反了。
------------------------------------------
长方形类是扩展的正方形类。用集合论中的韦恩图来表达一下会更清晰。长方形是一个大类,而正方形是长方形大类中的一个小类。用通俗的话讲,长方形类比正方形类来的大。
------------------------------------------
斗胆问一句:生物学上把所有生物分成 门、纲、目、科、属、种 几大范围。
按yangye1211(杨杨) 的话讲,长方形的范围比正方形大,所以长方形扩展正方形,所以长方形是正方形子类。 那整个生物界和人类谁的范围大? 人类是生物界的父类?
例子举的有点过,不过在谈继承这个词的时候,千万别比较谁的范围大。 因为正好相反,范围大的在OO里面正好是父类,虽然在java里,继承的关键字用的是 extends ,有扩展的意思。但那可不是范围上的扩展,而是功能上的扩展的意思。
如果非要和现实扯上点关系的话,那这个extends正好就是变异的意思。生物学上变异分为优变异和劣变异,劣变异就是我们通常说的退化。 extends在这里指的就是优变异。我们人类比其它灵长类动物都聪明,会制造工具等等。这些都是功能上的extends,而不是范围上的


哈哈,哈哈哈,笑死我了。(yangye1211(杨杨)) 学英语的初哥跑到这里谈论些什么呀!
无知者无畏。
哎,程序语言不等同于自然语言。不要浪费阎博士的时间,和你辩驳这些。我这浅薄者来和你说句话:“回去编两天程序,再发言。”


我喜欢听到不同意见,喜欢听到批评。但是说句自私的话,一般来说谁不是喜欢和比自己强的人讨论。
因此就回复一点,就是truezerg提出的第一点。

先不问到底该不该继承,上面这句话怎么说都是错的。
如果:因为长方形类extends正方形类
所以:正方形类的实例,确实is-a长方形类的实例
那这句话正好反了。

这两句话没毛病吧?
搞清楚类和实例。学习一下集合论。
"因为正好相反,范围大的在OO里面正好是父类"
关于这句,重申我的观点,吃饭本没有尴尬,看你怎么吃。任何话说得太绝对,它一定可能有点小毛病。
我学东西很慢,不能和楼上写过大程序的阳光灿烂兄比。见谅。
我只是觉得学编程,我要有专业精神。因为我是软件工程这个专业,不是随便写写骗骗外行人,所以时间上我也赔不起,阳光灿烂兄见谅。


吃饭的时候想起来的问题。
提出来给大家批评:
从长方形到正方形,不存在数据抽象上的差异。都是两条边长,int 长和int 宽。
存在的只是方法上的差异。设置长和宽的方法有差异,所以这里最好不要用派生继承,
而用复合继承。正方形是长方形的子类(在数学定义上),正方形等于长方形(在数据抽象上)
如果在数据抽象上存在差异,比如
class Vehicle
|
class WheeledVehicle class Boat
|
class Car class Bicycle
|
class Two-door class Four-door
那用派生继承比较好。
如果在数据抽象上存在差异,在方法实现上也存在差异。这样的例子也有吧。(虚函数)




to: yangye1211(杨杨)
其实关于长方形和正方形的讨论已经没有什么必要了。 我是针对你的那二句话来说的。 就是
-----------------------------------------
正方形类的实例,确实is-a长方形类的实例,为什么?因为长方形类extends正方形类!
长方形类是扩展的正方形类。用集合论中的韦恩图来表达一下会更清晰。长方形是一个大类,而正方形是长方形大类中的一个小类。用通俗的话讲,长方形类比正方形类来的大。
-----------------------------------------
你还是认为
如果:因为长方形类extends正方形类
所以:正方形类的实例,确实is-a长方形类的实例
这样的结论是对的吗?
写成代码是不是更清楚一点,比如:
class A extends B {}
你说 谁 is-a 谁? 是 A is-a B 对,还是 B is-a A 对。 子类的实例是一种父类的实例,所以应该是 A is-a B 嘛。 用长方形和正方形分别替换掉 A和B, 不就是
长方形类 extends 正方形类
那不就是
长方形类的实例 is-a 正方形类的实例嘛。 按你说的,不是正好反了?? 所以我说不管长方形和正方形(其实我们完全可以抛开长方形和正方表了)应不应该有这种继承关系。你的那句话都是错的。
我不明白你所说的集合论和类的继承有什么关系。 麻烦还请杨杨兄指教一下。
另外我说:
“因为正好相反,范围大的在OO里面正好是父类”这句话是针对你的论点而言的。 当然不是绝对的。 但按你的用“范围”这样的观点来推的话,在OO里面确实是范围大的是父类。 因为父类是一般的,子类是特殊的,换句话说,子类是有“个性”的。 个性的东西怎么能大于一般的? 而且你用“范围”这个角度是判断类的关系,这本身就是错误的。 原因我上面都说了。 如果杨杨兄还有自己的关点,希望能抽出点时间来这里说明一翻,大家好可以互相讨论学习。


呵呵,讨论又开始啦,大家加油
继承吗,就是为了我们coding起来容易,阅读起来清晰。
我认为并不一定要和集合联系起来,
当然一谈论到继承的关系,很容易让人联想到谁属于谁,谁包含谁
这样想起来是容易一些
但继承不是纯粹的集合关系,
A是B的子集,不一定A就是B的子类,也不能说B就是A的子类。
举个例子
太阳系是银河系的子集,但是如果让你用程序描述的话,它们之间能相互继承什么东西呢
但是非要和集合拉上关系,那么外界能看到的父类方法的确应该是能看到的子类方法的子集
我没读过专门的讲述OO的书,所说的一切都是我自己的想法,肯定会有错误的,希望大家指正。
并且希望能帮我推荐一本比较好的书,不要太厚哦,博士那本书我啃了还不到四分之一呢。


路过的,随便说两句。
同意asdmonster(asd) 的说法
个人感觉这里主要的问题是子类--父类关系 和 对象--类关系的区分问题。
说一点自己的看法。
类= 定义式
对象= 根据类的定义产生的具体实例,它不能违反类的定义,因此“一个正方形”不“是”“矩形类”的实例,但是你可以把它“当成”矩形。
子类= 被特化的类定义。它“基本上”遵循父类的定义规则,但是在某些地方使用了自己的定义规则。因此某些父类可以做的事子类可能不能做。
例如
类People拥有WalkTo(place)的能力。
类Man继承了People,但是可能在WalkTo(place)中增加了一些限制:
使得你调用a man.WalkTo(女浴室)时会报错。
现在有一个叫Jane的人,
当调用Jane.WalkTo(女浴室)时发生如下错误“一个男士是不应该去这种地方的”是不会让人觉得可疑的。


我觉得计算机是一种工具,因为有了信息化的时代才会出现计算机,为了操作者的方便出现了操作系统,然后呢就有了现在的应用软件,开发语言。 他们的存在是因为他们符合时代的要求,哲学上讲事物的正确性是有一定物质条件的,事物的对与错在物质条件发生改变的时候就会发生改变。为什么正方形要继承长方形因为,因为在OO理念这的确是对的,那么在数学理念里这有是错的,这就产生了两种不同的物质条件。


OO只不过是一种设计思想,他的存在是为了我们更好的设计,所以我们没有必要去讨论对与错,只能说怎么样才能做出更好的设计,而更好的设计是根据你的需要而改变的,有可能你的系统中需要resize()方法,而我的系统中并不需要resize()方法,所有这样产生的结论也会不同,我觉得非要定义谁去继承谁并没有实际的意义,需要根据具体的情况而定


“面向对象方法的本质,就是主张从客观世界固有的事物出发来构造系统,提倡用人类在现实生活中常用的思维方法来认识、理解和描述客观事物,强调最终建立的系统能够映射问题域,即:系统中的对象以及对象之间的关系能够如实地反映问题域中固有事物及其关系。”
上面这段话你可以在任何关于面向对象的理论的教材上读到。
这是oo的总纲。
呵呵。工具,这么小瞧工具呀!我们就是伺候工具地。计算机是骨骼,我们是肌肉。连为一体。这就叫做人机合一。不是你想不想合的问题。
具体情况就是现实中地问题域,设计中不符合就改掉。削足适履?否,这叫适应环境。


使用"is-a"和"has a"测试。
长方形 has a 正方形
正方形 is a 长方形。
通过,所以这样的继承关系是正确的。


呵呵,大家自由讨论,不要有压力。


在UML出现之前,确实有很多图,其中有一些现在也可以用。譬如Venn Diagram,就是集合论中的那个,就可以用来描述一个类的responsibility。
参见
Designing Object-Oriented Software
Rebecca Wirfs-Brock...
1990 Prentice Hall


我们班上有两个同学我比较喜欢与其讨论学术话题。N节日语课都被浪费在无休止的无意义的(至少在旁人看来)讨论上了。真对不去漂亮的日语老师MM啊。
小曾是个山东汉,运动会上铅球随便一扔就为我们班拿了个第一,但是他今年没去年扔得远,没有破掉他去年创下的院纪录。其实我每年都让他悠着点,这样年年都有记录破,我们班的分也会更高,可我怀疑他在铅球场上一使劲就忘了。这家伙在日语课上看j2ee那本红书的时候回过头对我说他立志于设计和模式,我藐视地对他说,设计已经睡了。真的已经睡了。(Martin Fowler说设计已经死了,但我认为设计没死,但是睡了。)他瞪大了眼睛对我说,胡说,就说上次那个操作系统的文件系统作业项目,全班就没人设计的比我好。我说,拜托,不要跟班上的人比(潜台词是其实班上懂设计的有几个,将来都是做TechnicalSupport和Marketing的料),随便拉一个在公司干过2年以上的,做的肯定比你好,有点追求啊。我和他说,要实践才能出真知。现在人类刚刚学会造房子,绝大部分人都在造方方正正的那种房子。方方正正的房子谁不会设计,只要是个工头他就会设计。你说你能设计悉尼歌剧院,一是没有哪家公司有那么多高智商的建筑工有本事去建,二是还没有这种需求。既然大家都是四四方方的房子,那大家也都觉得挺满意。(第一个原因是主要的,因为生产力决定生产关系嘛,等能建这种房子的公司多了,自然会有富人想住设计超前的房子来满足自己对优越感的追求的)所以我说,设计睡了。你还是多实践实践啊。他的回答是,我不需要实践,只要项目摆在我面前我就能做,而且我对做项目也不感兴趣,我的目标是分析到中层。这点我相信。Gates现在让我设计下一代操作系统我也能啊,支持B2D(Business to desktop),支持超级代码托管(在某些情况下,根本就不生成代码),支持SSL3.x with RWPI(实世界个人身份确认),强不?肯定强,Gates也要说我强,但是我要100年才能完成中层设计,Bill一定会将美国国骂脱口而出。
后来我就把这个帖子的事情说给他听,他脱口而出就是应该把正方形和长方形看成一个类,然后又举了MFC做例子。小子好样的,证明我没看错人(虽然离我还有点差距,我还知道DNA哈哈哈哈)。然后他的感慨就是不能为了继承而继承,很多人现在是轻设计,重coding,我也有同感,其实面向对象啊,数据抽象才是灵魂。就像一个人来讲,他的DNA决定了他是他,决定了他哪里多长一块肉,哪里少长一块肉。他是谁,他属于哪一类,并不是由他的行为,他做了什么事情来决定的。差距啊(至于谁和谁的差距,见上面那个“吃饭”的典故)。然后又讨论了一点其他话题。
今天和惠猪的讨论比较简短,这家伙和我们俩个完全相反,整天埋头做PHP项目。票子赚了不少,可是我觉得他应该多多参加我们的讨论,毕竟他对j2ee的理解还停留在jsp阶段。但是我觉得他比我们俩个在有些方面都强,至少有实践机会,我很多东西都要问他。我就一直想找个实践机会以后面试问起来也光彩点,怎么说也算个工作经验啊。现在有些人用java做了点东西就牛得不行,其实那些东西用VB做做也可以,瞧不起VB程序员就因为人家比你早出来混两年,你看,这什么逻辑。


这个问题搞的有点个人味道很重了。
年轻人有火气挺正常,但也看清楚世界。什么vb早混两年的话也出来了。你问问有多少java程序员是直接上来搞它的。还不是都从vb,c++这些上转过来的。我就搞了4年的vb。java也弄到第三个年头了。别人更不用说了。到这里发言的那个不是身经百战。
讨论问题,就讨论问题。其实这个设计也可以用复合。但是我的意见还是:继承关系是成立的。设计是艺术,是可以灵活的,有多种选择的。


中国软件业最大的悲哀就是:程序员太多了。


这是站在技术和雇员的角度说的。


to 杨杨:
..................
正方形类的实例,确实is-a长方形类的实例,为什么?因为长方形类extends正方形类!
长方形类是扩展的正方形类。用集合论中的韦恩图来表达一下会更清晰。长方形是一个大类,而正方形是长方形大类中的一个小类。用通俗的话讲,长方形类比正方形类来的大。
它继承了正方形类的DNA。
现在看看class Square : extends Rectangle 是多么愚蠢!
明明应该是 class Rectangle : extends Square
..........................
我不同意这样的说法。这不颠倒吗?正方形是长和宽一致的,所有的长方形都有这特性?


。。。。。。。。。。。。。。
至少在DNA上,和母体相当。java里面用了extends这个词,愚以为一语中的,extend在英文里有“扩充, 延伸, 伸展, 扩大”的意思。回到长方形和正方形的问题上,认为正方形是长方形子类的同学可以考察一下正方形这个类的DNA(开玩笑),正方形extends了长方形没有,显然没有。正方形is-a长方形,没错,难道不应该继承吗。注意,我们在讨论类,没有讨论实例。java教科书中对类的定义大致为:
。。。。。。。。。。。。。。。。。。。。
就extends一词的理解,我想你把它当成“include”了
“延伸”,我认为是extend最确切的涵义,何为延伸?起码得保证本体吧,如果本体的性质都不符合,那叫什么延伸?


。。。。
正方形extends了长方形没有
。。。。
把这里的长方形说成是矩形,应该正规一些。
正方形就是在符合长方形的所有共性的基础上,多了一个长和宽保持相等的特性。这才是所谓“扩展”,我认为这正是"extends".


刚发那贴的时候,我只能理解到这个程度。
我那贴只是说了点别人还没看清的问题,但是回避了我看不清的问题。
现在我发现哈哈,等同的东西,谁大谁小?
你的意思是长方形>=正方形
那我是不是可以从你上面的话里推出 长方形<=正方形
还是那句话,不要用人的行为来给人分类。“它不能进女浴室所以它是男的”,这不是因果颠倒了吗?
博士和学士的差别就在于博士知道二叉树和最多有两个子树的有序树之间的区别。
(个人看法,我本科还没毕业)


就大小问题,我先不作结论,就下面的话:
。。。。。。。。。
那我是不是可以从你上面的话里推出 长方形<=正方形
。。。。。。。。。
怎么推?还请发表高见。


to 自己:
把这里的长方形说成是矩形,应该正规一些。
正方形就是在符合长方形的所有共性的基础上,多了一个长和宽保持相等的特性。这才是所谓“扩展”,我认为这正是"extends".
所谓言多必失。刚才这段话,现在想起来是有问题的。其实在上面已经讨论很多次了,而这句话又走了老路。因为就算是讨论数学概念,正方形概念应该小于长方形(矩形)。也就是说,长方形里面包含有正方形这种特例了。正方形再从长方形继承(或者说扩展extends),看来是不好的了。


我自己现在差不多已经搞清楚了。大家还要不要继续讨论?


没关系。列宁说你越想驳斥真理就越发现它的正确性。


感觉在解读哥巴德赫猜想似的,有意思


TO:yangye1211(杨杨)
说实话, 你对extends的理解是有些偏差的。 给我的感觉你总是在比较谁大谁小, 继承关系不是看谁大谁小来决定的。 extends是扩展,前面我已经说了。 这个扩展是指功能上的,行为上的,能力上的,不是你所说的范围上的,正象 fbysss(独孤求败) 所说,你把extends当成include看来了,这是错误的。另外我也不赞成继承关系靠抽象数据来获得。 抽象行为更是获得继承关系的依据。


今天大概翻了一下《JAVA编程思想》,觉得我错了。
从正方形到长方形,使用继承,向上转型是安全的。
所以应该使用继承。
但是要使用正确(指符合逻辑)的覆盖的方法。
子类开发者任重道远。 :—)(所幸需要继承的情况比较少)


up


no,no,no,我们可不是说你错在正方形不继承长方形上。 事实上应该不应该继承要分情况而定。 我所指出您理解有误的地方是指:
1.范围大的继承范围小的,这样一个思想
2.如果 A is-a B,则 B是A的类子这个错误理解(您理解反了)
关于正方形到底该不该继承长方形有它的特殊情况和应用环境在里面,纸上谈兵对这个问题没有统一用处。


其实这个问题可以追溯到java里对象中的非静态成员如何分配内存的问题。
在这里就不展开了。
欢迎楼上各位补充。谢谢。


关于正方形到底该不该继承长方形并不是一个可以用“特殊情况”或者“应用环境”来搪塞的问题,这个问题的分歧说明了目前软件从业人员鱼龙混杂的现状。


if you wanna discuss the super-subclass relation between 'rectangle' and 'square' implemented in teaspjava/c++ into abstract (or even mathematical) level, you are doomed as a programmer.
mathematians, linguistics and computer theory professionals work in this area, with some research result goes down to programming level. they talk about abstract data type or inheritance theory in different levels,and pay little attention to programming lanugage and machine implementation.
we as programmers talk about single inheritance implementated in teaspjava/c++, and we stop here. inheritance is a 'is-a' relationship,and 'is-a' is defined by substitutability. declare class 'rectangle'then define class 'square' as its subclass. the requirement here is: everywhere you call a method of 'rectangle', you MUST be able to call the same method on 'square'. rectangle is super cuz you know more less about it then square. e.g., you define a method calculateArea() for rectangle, as area = lengthOfSideA * lengthOfSideB, then you know square as a subclass must also provide this method, either by inheritance, or by overriding.
the reason you define square as a subclass of rectangle, is that you know MORE about it than rectangle. in C++ words, super class promises less, sub classes promise more. you know more about square beyond rectangle: you can simplify calculateArea() as area = lengthOfSideA * lengthOfSideA.
but sometimes the 'more' information is about 'unknown' or 'contradiction', making the system more comlicated and even inconsistent. Remeo knew Juliet the first time as a beauty and fell in love with her. fine, a simgle and perfect world, a method marry() { while (true) { liveTogether;}} is defined; but later he knew more about her: the daughter of his family's enemy. Ok, you have to update your former default behavior marry(), as either { throw new exceptin(No marriage with feud);} // as suggested by his family members,
or { poison Juliet; take her body out; meke her recover; live together; }
but the 2nd method unfortunately throws a sad exception: DeathOfJuliet.
So you also need to modify the signature of the original method marry().
the lesson: see next post


so here is my point: a simple and consistent single inheritance mechanism can NOT model the complicated, inconsistent, and unlimited world with so many unknowns. OO is a simplified programming methodology making the tradeoff between modeling ability/declarativity and machine code infficiency.
inheritance is a powerful modeling and computing mechanism for 'is-a' relation, but it is not powerful enough to cope with system inconsistence in a single step. subclass can pontentially change and even defy the default behavior defined in super class. calcualteArea() is an example of change. method increaseSideAButKeepSideB(), and marry(), are examples of refute. How to work around? while, 2 ways, both admitting it is not a perfect and simple world:
-- break the relation: a square is not a rectangle, a white horse is not a horse. Use composition instead, dont touch known inconsistent behaviors (side of square, color of white horse).
-- overriding: try to embrace the more info found in subclass (bleach the black horse);and if cant, admit the failure and throw an exception.


TO:yangye1211(杨杨)
关于正方形到底该不该继承长方形并不是一个可以用“特殊情况”或者“应用环境”来搪塞的问题,这个问题的分歧说明了目前软件从业人员鱼龙混杂的现状。
_____________________________________________________________________________
不要乱扣帽子,大家是在讨论问题,并不能就说明什么现状
下面讨论问题
举个例子,你认为长整形Long和整形Integer之间是什么关系?
Long的范围包括Integer的范围,
也就是说,Integer是Long的子集
按照你的理论是不是要继承一下呀?
但是SUN并不这么做
他们是并列的,都继承自Number
我并不是说SUN做的东西就完全正确,
但是到目前我自己还没有看到对这种做法有异议的言论


我的理论有没有人理解?
我的理论是长方形要继承正方形?
还是理解的人已经不发言了。
我们这里不管sun是怎么做的,sun不对我的面向对象课程成绩负责,也不对任何学术讨论负
责。因为人既可以从性别的角度来划分(有男人,女人,清华的女博士三类)也可以从国籍的
角度来划分(比如中国人,非中国人,假洋鬼子三类),所以把sun或是什么跑不跑题扯进来
是没有意思的。长方形和正方形在数据抽象上是一样的。在数据抽象上:

长方形=正方形。
好,那么长方形>=正方形 和 正方形<=长方形都对了吧。
就说这么多,继续看Inside The JVM


哦,是长方形<=正方形,笔误


Mr. jiganghao(JH) :
Will the overriding be helpful in our problem?
If true, how does it take an effect?Thx.


--------------------------------------
关于正方形到底该不该继承长方形并不是一个可以用“特殊情况”或者“应用环境”来搪塞的问题,这个问题的分歧说明了目前软件从业人员鱼龙混杂的现状。
--------------------------------------
晕~~~


yangye1211(杨杨)所说的观点有一些新颖的地方。


to yangye1211(杨杨):
as mentioned in my former post, overriding is one of 2 ways you go when a default inheritance between super and sub class is invalid.
an inheritance is invalidated when its rule is broken. This rule, IMHO, is not set theory (A includes B so A is super class of B), nor 'is-a' relation ('is-a' is a tuition, not a computing model for OO langues as teaspjava/C++). the rule, is substutability: if class B is subclass of class A, then every place you meet an instance of A, you can safely substitute it when an instance of B.
so my answer to super/sub relation of rectangle and square is, as most people here claimed, it depends. in you closed program context (i.e., your software system), if square can always substitute rectangle validly, then square can be defined as subclass of rectangle, square 'is-a' rectangle, white horse 'is-a' horse. if substitutability is not satisfied, then sqaure is NOT rectangle.
In the latter case, you either breaks the inheritance (composition is an alternative), or you keep the inheritance but have to override certain methods (e.g., current president general Clark is a member of democratic party, but he voted for Bush in year 2000).
OO is an effort to model the world in a nature way, but the natural world is perfectly simple and consistant. there is mutation. Is an ABC (american born chinese) a chinese? if yes, you may have to modify the behaviors defined in super class 'chinse', e.g., speakChinse(), if ABC cant speak chinese. you keep the relation btween 'chinese' and ABC by way of overriding.
while, if ABC changes too much from chinese, you may say an ABC is an american, not a chinese anymore, like some taiwaneses are claiming. Human is an example: s/he originates from apes, then evolved advanced intelligence and skills, up to a certain point that s/he doesnt belong to apes any more. continous evolvtion (mutation) accomplished into a brand new human kind, an evolution.


i am a chinese, inherite most behavior from my parent, although override a lot also.
I am not sure how much overriding will happen on my children; but even if /she breaks the chinese inheritance, i'll make sure at least composition works ;)


TO:kaguo(Faith)
你误解我的意思了
我举这个例子就是和yangye1211(杨杨)说,并不是有集合关系就要继承。
所以正方形和长方形之间的关系,不一定非的要用继承关系。
用不用继承是和具体情况有关系的。


TO: yangye1211(杨杨)
其实这个帖子刚开始讨论的时候我也考虑过让长方形继承正方形,但是考虑之后觉得,这样继承只在非常特殊的条件下才成立
1. 长方形的边长(长和宽)符合一定的函数关系,正方形中边长使用一个变量来表示,设置边长用一个方法就可以了,但长方形有两个边长(长和宽)呢,怎么办,一个方法同时给两个边(长和宽)赋值吗,那么这两个边(长和宽)就只能符合一定的函数关系了,这样才能把本来是给一个值赋值的数赋给两个值?我想你的想法也许是在长方形里重新写给边长赋值的方法,那么面积呢?也重新写吗?那么这样有哪些不用重写呢?四条边和四个内角是直角。呵呵,那就去看看我2003-11-11 10:56:19的留言。
2. 只能有长方形和正方形这两个类,或者说是有很少的类。否则,会有长方形继承正方形,平行四边形继承长方形,四边形继承平行四边形……,那么四边形从正方形那里能继承来什么呢,设置边长还是求面积?这次连内角都不一样了。基本上所有的方法都不相同,都要重写,那要继承有什么用呢。
所以我说正确的继承是有条件的。能否正确的继承要看具体环境和你要实现的功能。


不能乱继承,要符合"is-a"原则。有继承关系的但是可以不处理成这种关系。反之不可。


根据长和宽可以唯一确定一个长方形,根据长和宽可以唯一确定一个四边形吗?不行的呀,四
边形多了一个数据,就是内角角度。用四边形继承长方形,那不是问题大了。
至少要知道一个内角的角度,再加上两条边长,才能唯一确定一个四边形。因此四边形
的数据抽象和长方形不同了,如果继承了,那么以后难免会出问题。比如求面积。
重点来了:
小学数学老师一定告诉过你:“判断一个整数是否能被3整除,只要看它各位数字相加的和
是否能被3整除。”这个法则完美解决了判断一个整数是否能被3整除的问题,但是这不是理
论,但这只是经验法则。身为小学生的你,只是照着这个去做罢了。理论的证明还是要留待高
年级学过了一些代数知识才能解决。这个规则告诉你怎么判断,但没有告诉你为什么。
现在搞清楚理论和法规的区别?
法律规定,盗窃是有罪的。所以你不能盗窃,但是法律有一点点小缺陷——它没有告诉你为什
么盗窃是有罪的。
LSP告诉你,违反LSP的继承是有罪的。但是LSP没有告诉你为什么。
所以有了这个帖子的讨论。
其实,LSP只是法律,并不是真理。
在和平时期杀人有罪,在战争时期杀人未必有罪。
抱着表面的LSP不放,必然会产生楼上各位的诸多烦恼。
一个Object大概可以分为两部分,它的数据和它的方法(行为)
LSP如果更清晰地来表述,应该是,
1)在方法可以被覆盖的前提下,子类的数据抽象一定要和父类的数据一致,否则请用
composition。
2)在方法不可以被覆盖的前提下,或是不提倡子类方法覆盖父类方法的前提下,父类出现的
地方,一定要可以用子类来替换,否则请用composition。
以上是我对LSP的归纳。
我们这个问题,是子类可以覆盖父类的方法的前提下,所以,不要用第2条来判断正方形是否
可以继承长方形。请用第一条。
欢迎指正。


第一次来java学习,就看见了好贴子,忍不住也说说自己的看法
在说 A is-a B的时候,是必须要考虑到B和A的方法和属性,单纯的说A is-a B是无意义的。
在设计中,我们设计长方形和正方形的时候肯定是要为这两个类定义方法和属性的。我觉得只有在定义了方法和属性的基础之上我们才能考虑类之间的关系是不是继承关系。脱离方法和属性是没有意义的
大家指教


大家别讨论正方形和长方形的关系了。 单纯的讨论这个没有什么意义。
不如大家谈谈在你们各自的实际项目中是如何处理类之间的关系的。可以举实际中的例子。并说说自己的分析过程。 我想了解别人分析的过程比单纯的争论一个结果好得多。


不知道这算不算统一了?


我赞成qiujoe(迷糊) ,我也觉得如果前提都不一样讨论就没有价值了。


jiganghao(JH) :
LSP确实是进行好的设计的指导原则,但所有的规则都是可以破坏掉的。如果LSP强制执行的话,Java语言就没有用了。任何一个Java类都是java.lang.Object的子类,几乎所有的Java类都需要有Object所没有的方法和属性。
因此研究LSP的方向是在什么时候应当遵守,什么时候应当破坏。这个研究就导致了DbC,而Contract就可以作为一个集合描述,因此使用Venn Diagram是很自然的。
你所说的,长方形和正方形的关系depends具体情形的观点我是同意的,这个分析的准则也应当是DbC。你写一个Java类,这个类和Object类之间的Contract不限制你给出更多的方法和属性,因此你可以这样做。在长方形和正方形的情形下,这个Contract是什么,在具体情形下有所不同。根据这具体的Contract,长方形和正方形可以是子类-超类、超类-子类、同属某超类的子类(sibling)的关系。
我的书中并没有讲这些,因为我不想花太多的章节讲解DbC。DbC本身就可以写一本书了。


jeffyan,你说java的Object单根违背LSP?这个说法很有趣,不知道根据是什么?
另外,我不认为这个问题和dbc有什么联系。它是纯粹理论上的类型系统协变原则而已,而dbc则是运行时的,是当类型系统不够处理复杂的约束关系时才用到的。


i feel it enough for discussion of relation between rectangle and sqaure. we are now talking in the areas of 程序语义学,代数逻辑 and/or 知识构造模型, which is way beyond practical design pattern ideas, which is based on executable OOP like C++/Java. as a programmer, i am now more interested in design patterns, XP, RUP, and agile programming.


早上一边和同事讲话一边打字,没有讲清楚。LSP有两种形式,一种较强,一种较弱。我书中讲的就是弱LSP,强LSP要求父类和子类的接口相同。
Java对象树的单根性违背强LSP。
如果一个问题与LSP有关,就一定与DbC有关。


google了一下,没有发现“强LSP”的相关资料。只有一个简单的替换原则的定义:
FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.
从这个定义出发,Java的Object是完全符合定义的。
这个替换原则,也就是和OO类型理论里面的subsumption定义一样的了。也就是说,符合LSP的,才能是subtype。
至于你说的父子必须具有相同的接口,恕我鲁钝,不知道这样要求的意义何在?所谓“强”,又强在何处?能不能给一个“强LSP”的定义?
还有,网上是有一些文章是和DBC和LSP相关的,但是,那都是本身在讨论DBC,而DBC的基础就是要符合LSP。所以,要研究Dbc不搞清楚LSP自然是不合理的。
但是,这不能反过来说要研究LSP就要研究DBC呀。可以说LSP是父类,DBC是子类。这个依赖关系不能倒过来呀。
这个问题本来就是只跟LSP相关,是静态类型领域的,跟动态的DBC的关系只怕很牵强吧?除非你的"DBC"不是eiffel那种意义上的DBC。


爱因斯坦说我们就像一个走进图书馆的小孩,仰头看着高大的书橱,知道这些书一定是按照某种顺序排列的,可是我们不知道是什么顺序。


在自然科学的领域,人类还处于幼年。


别讨论了,说句不好听的话,各位对类的概念还没完全搞清楚!


那楼上的解释一下什么是类吧。
要不来个具体的,解释一下什么是人类?


你还真跟我叫劲了!什么叫解释一下人类!
你把类的概念写出来,我一条一条给你解释!


我总觉得模式很难很难,唯有不断重构


大家好,我就是你们讨论的正方形,本来我和长方形井水不犯河水,但是你们苦苦相逼,长的像就一定有关系吗,那要看不同的应用了


这个帖子我很久以前留言过,没想到争论到今天还没有结果。
一个帖了这么久的帖子,我原以为有些高手会留下他们的评论,但是很遗憾的,我看到得不多,所以我看到这些争论的时候,我想起了一个名词,学院派。OO,LSP,DBC...如果你不拿来写程序的话,估计全部的价值也就是在论坛里面炫耀谁看的书多,谁的记性好。
看了大家的帖子,我终于明白了,其实我们大家都不错了,长方形和正方形没有关系,如果有也只是兄弟关系。
有三个概念:矩形,长方形,正方形。
矩形是后面二者的父类,代表了拥有两对垂直边的四边形
长方形:两边不等的矩形
正方形:两边相等的矩形
其中需要提醒的是矩形保证的是有四个边两组,setWidth(),setHeight(),但是他并不假设二者之间的关系,二者之间的关系留给了子类:一个相等(正方形),一个不相等(长方形)。矩形保证 area == height*width,但是它并不承诺正方形的 area == height*height == width*width,并不承诺长方形的 height != width ,正方形setHeight()和setWidth()以后area = height*width依然成立,因为父类并没有相关的承诺:改变width以后还维持原来的height,
(可惜我的英语不好,不知道怎么用两个单词区分矩形和长方形,所以没法子写出示例代码)
集合的包含关系完全可以表现为继承上的父子关系,就象属于关系演化为聚合关系一样。
但是基于上面的理由,长方形和正方形没有任何关系,比较长方形和正方形的多少也是没有意义的,就象,你说整数和小数哪个多?
我虽然喜欢c++,但是丝毫不影响我认为java同样是一门出色的语言,特别是对OO的贯彻上。
建议大家看看一些经典的关于java中 interface 和 abstract class的区别的讨论,也许比讨论“白马非马”有益得多。


受教受教了。
楼上的意思大概是说使用矩形这个抽象类吧。这也是我们讨论过的方案之一。但普遍的看法是
这个方案其实与我们的讨论没有太大关联。
我觉得如果能为正方形找到合适的setX(),setY()方法,那么这个问题就解决了。
实际上不难。比如将setX()和setY()设置为抽象方法,在使用正方形这个类的时候根据情况
适当加以实现。(即,如果你不能确定使用者将如何使用,那么干脆留下一个接口好了,这也
符合开闭原则的精神)
所有的罪恶都来自子类的创建者:-),因为你们覆盖不当。
如果你们找不到完美的可以确保万无一失的方法来覆盖父类中的方法,那么就应该把问题抛给
你们的下一级。
反之,你可以确定没有问题的方法,那你应该承担起你的责任和义务。
PS :
楼上连interface和abstract class都出来了。那篇讨论三个原则的文章流传的也真够广。
请问“承诺”为何物?莫非是asdmonster(asd)兄自创的语言?
请教我怎样在使用您的基类(or接口)时可以得到您的所谓“承诺”?
我可以负责的告诉你,没有你所谓的“学院派”,就不会有计算机。
更不会有编译器,数据结构,操作系统。
现在OO研究中就是缺少数学理论的支持。
世界上最有钱的是商人,最有知识的是学者,最有实践经验的是工程技术人员。
这三种人对软件乃至计算机的发展都是缺一不可的。


Ajoo:
读一读Liskov 和 Luttag的书。LSP本就是我最早引入到中文图书中的,当初没有提出这个强和弱的概念。


jeff, 给个书名或者连接吧。谢了。:)
其实,我是挺期待你能在这里给介绍一下这个强lsp的概念的。我实在想不出让儿子必须和父亲一样有什么意义。能否照你的理解给个例子?(我发现例子比直接的定义好理解很多)
我search的可是“strong lsp”,不是“强lsp”,所以应该不仅仅是中文图书拉。但是仍然是一无所获。似乎当初Liskov也没有提出“强弱”的概念吧?


关于LSP与DbC的关系有很多讨论,我不想重复了。最为激进的讨论认为LSP和DbC是同一个东西两个名字,也就是AKA的关系;较为中肯的,譬如Uncle Bob认为二者closely related。如果你认为两者关系很大,那你基本上就和大多数人没有什么区别,我也没有什么好问的。可如果你认为两者没有关系或者关系不大,那么你的观点就较突出,是不是需要说明一下理由。
DbC描述Contract下的设计,因此是静态的。一个Contract包括前条件、后条件和不变量,这三者都应当在设计阶段给定,除非软件需求发生变化或者进行重新设计,不然是不会改变的。对这个感兴趣的朋友可以看看Java iContract插件,它直观地显示了DbC的概念。学习DbC不需要学习eiffel,这是一个常识。
一个方法在被执行的时候,必须满足前条件,在执行之后,结果必须满足后条件。假设基类A和子类B形成继承关系,两者都有一个方法m。那么B.m()的前条件必须较A.m()的前条件弱一点(或不变),B.m()的后条件必须A.m()的后条件强一点(或不变)。
以DbC为出发点分析一下长方形和正方形的关系,就会发现正方形的后条件比长方形的要弱。因此在某些情况下,只要能保证二者前条件相同,那么yangye所说的就是正确的:长方形是正方形的子类;如果不能保证这一点,那么二者就不能有继承关系,而只能是sibling的关系了。当然,如果一般地表述,使用sibling的关系总不会错。
至于LSP的几种形式,如我前面所述,请读读Liskov和Guttag书中她本人的描述。其他情绪化的问题我就不接了。
呵呵,大家讨论。


晕,一定要我写成你们熟悉的方式?
整数是小数的特例吗?不是。相等是不等的特例吗?不是。因此正方形是长方形的特例吗?不是。为什么,我不是数学系的,我无法准确的告诉你在哪年哪月哪个数学家的哪篇文章里面说过。如果你认为是而且需要我信服,请给出论证。
同上理,如果认为正方形的父类是没有限制长宽关系的矩形而不是已经限制了长宽必须不等的长方形,并不违反BCP,LSP,说到底,正方形和长方形的父类并没有这样的Postconditions:setWidth()(或者setX())以后Height(或者Y)并不改变。但是换句话说,一定要硬套,那么,正方形的那四条相等的边,到底是对契约的强化还是弱化?都不是,相等既不是不等的强化也不是不等的弱化,二者是两个并列的关系。


我提出interface 和abstract class不过是觉得这个问题的讨论很适合阐明Object,不是吗,要是各位都明了了什么是Object,知道什么应该extend的,什么才是用来implement的contract,还用在这里喋喋不休?
当然,本人才疏学浅,自创不出一个“承诺”来。
路过,随便说两句闲话,各位自便吧。


to: jeffyan77
假设基类A和子类B形成继承关系,两者都有一个方法m。那么B.m()的前条件必须较A.m()的前条件弱一点(或不变),B.m()的后条件必须A.m()的后条件强一点(或不变)。
能举个例子说明一下这句话吗?
我不会对你的例子进行反驳,只要能让我明白你的意思就可以了。 随便举


我不反对别人反驳我的意见,我只是不想被别人拖入到我不想卷入的辩论中去。譬如我本来也不想在这个地方介绍DbC,可现在身不由己。好吧,下面沿用你的代码例子
下面是你的长方形:
private int length;
private int width;
public void setLength(int lenght) {
this.length = lenght;
}
public void setWidth(int width) {
this.width= width;
}
public int getArea() {
return this.length * this.width;
}
注意setLength()的后条件是什么?是
(1)length=新的length
(2)width=老的width
下面是你的正方形
public void setLength(int lenght) {
this.length = lenght;
this.width= lenght;
}
public void setWidth(int width) {
this.length = width;
this.width= width;
}
注意setLength()的后条件是什么?是
(1)length=新的length
这就是说"正方形的后条件比长方形的要弱"。
有朋友会问,正方形的后条件是不是有两条
(1)length=新的length
(2)width=新的length
这样一来是不是就可以说"正方形的后条件比长方形的要强"了呢?不能,因为长方形的第二条后条件在这里改掉了。
这个地方是不是可以使用Venn Diagram?当然可以。
可能有朋友会问,你是怎样决定后条件的呢?是不是有随意性?答案是要根据对象的状态和使用端的需求。
(1)你这两个类只有两个状态:长和宽。
(2)使用端的需求建立在长和宽基础之上。
如果使用端不依赖于宽,那么width属性没有存在的必要。使用端根本不会发现setLength()后条件中width的变化,那么这个后条件就可以去除,这样一来长方形和正方形可以互为基类,或者根本没就必要分成两个类。或者使用端给出新的需求,并衍生出新的后条件,用来决定谁是基类。
继承关系必须建立在类的行为的基础之上,而行为的前后条件只能由对象的状态描述。
我们这里讨论的气氛一直很好,比如很少有那种“辩手”,只关心把别人辨倒。自己没有建设性的东西,只凭一点小机智、小聪明,专挑别人的细小破绽。谁都不是神仙,这里也不是大辩论论坛,大家讨论问题也是以弄懂技术问题为目的,没有其他目的。遇到这种人我就说明我的技术观点,然后免战。因为我没有那种功夫,也没有那种肾上腺素,辨倒我也不是什么大奖。我这只是一般性的说说,我还没有发现我们这里有这种人。
祝大家节日好!


我一直相信实践是检验真理的唯一标准


jeff, 我想我的观点说的很明确了吧?
就是说dbc的一些规则依赖于lsp。但是lsp并不依赖于dbc。
lsp仅仅是说子类的对象必须能当作父类使用。并没有更多的外延。
而这个概念应用到语言的静态类型系统上,就是所谓的subsumption和协变规则。
应用到动态的dbc系统上,就是前置后置条件的强弱关系。
画个图呢,就是这样:
Subsumption--------------->LSP<---------------Dbc
两者都依赖LSP。
而我的理解是,我们在讨论本来没有涉及dbc的静态的类型is-a和LSP的关系。而这只怕扯不到DBC上去。否则只怕所有的对类型系统的讨论都要涉及dbc了,这明显不符合事实。除非你所说的dbc更为广义,包括动态的前后置条件,也包括静态的类型约束。
至于lsp的概念,既然不想介绍就算了,肯花那么大篇幅给truezerg介绍前后置条件的协变关系,却不肯给我介绍一下强lsp,偏心啊!:)
不过我舍不得为了这个就买本书,找不到网上的资料,只好先这么无知下去吧。


回复人: yangye1211(杨杨) ( ) 信誉:100 2003-12-10 01:40:00 得分:0
我一直相信实践是检验真理的唯一标准
-----------------------------------------------
呵呵,我看最脱离实践的就是你了,正方形应该不应该从长方形继承还是让他们从一个共同的抽象类来继承又或是完全用同一个类来表示它们,答案只有一个:看实际情况。看我们关心的是哪个方面。
分类是一门学问,同一头牛,你是把它分到“家畜”还是“哺乳动物”还是“有蹄类动物”,完全取决于你的具体应用,看你关心的是什么。甚至它有哪些属性也是从实际出发啊。假如我们给奶牛场编一个“牛”类,就应该有产奶量、年龄之类的属性,但是要是我们给屠宰场作程序呢,产奶量还有意义吗,这时候就应该是产肉量了吧。总之,不同的应用关心的方面不同。
还有你说:“拜托,不要跟班上的人比(潜台词是其实班上懂设计的有几个,将来都是做TechnicalSupport和Marketing的料)”
----------------------------------------------
年轻人牛气冲天是好事,但不要靠贬低别人来抬高自己。


长方形和正方形的问题很可能是因为《Java与模式》而引起如此多的关注,讨论对于启发思考是一件好事情。这个问题在西方也一再引起讨论,本身已有公认的结论。结论我在书中已经讲过了,在上面的帖子里也作了附加的说明,不想重复了。
OO语言设计中最为困难的是什么?类型。Eiffel语言最早的设计就有类型设计的逻辑错误。LSP和DbC都是为了给类型设计提供指导而出现的,它们本来自于两个敌对的(这个字眼太强了一点)研究组,一个是Liskov组,一个是Meyer组。这两个组的东西在逻辑上是绝不可能相互依赖的,DbC依赖于LSP的可能性是不存在的。
LSP带有强烈的Defensive Programming的味道,DbC则是Blackbox testing的味道。它们的目标相同,结论如果不是完全相同,肯定也是非常接近,但是尚无法证明它们是完全等价的。有一点可以肯定:LSP和DbC都是类型设计的指导原则,任何时候只要有超类和子类,就需要考虑LSP和DbC。
LSP的表达看上去简洁,易于理解,DbC则带有相当多的佐料,让人家以为这是什么动物。实际上透彻理解LSP并不像看上去得那么容易,理解DbC不像看上去那么难。譬如Liskov 和Guttag的书中花费了相当的篇幅讲解抽象化、类型和LSP。LSP可以分成为三个规则:
signature rule
methods rule
properties rule
超类型可以分成三种
Complete supertype
Incomplete supertype
以及Snippets
LSP应用到不同种的超类中去,就有了我所说的强弱之分。
LSP可以直接应用到测试中去。LSP的外延很深很广。
迄今为止,绝大多数的教材谈到LSP基本上都是转述别人,很少有人亲自阅读Liskov和Wing最早的研究论文和后来的著作,对于DbC也是类似。以讹传讹,不求甚解。江湖越少,胆子越大。
这个帖子我不会再来了。呵呵,大家讨论。


我不同意根据实际情况来选择解决方法的方案。
因为事实就是事实,不能因为你还没有了解他,就以错误来代替。
其实OO就是要比较理论化,系统化的研究才行。


有种!
既然如此,我也不来了


回复人: yangye1211(杨杨) ( ) 信誉:100 2003-12-10 01:40:00 得分:0

我一直相信实践是检验真理的唯一标准
回复人: yangye1211(杨杨) ( ) 信誉:100 2003-12-10 18:33:00 得分:0

我不同意根据实际情况来选择解决方法的方案。
因为事实就是事实,不能因为你还没有了解他,就以错误来代替。
其实OO就是要比较理论化,系统化的研究才行。
------------------------------------
前后矛盾啊老大
这次真的不来了



搞清楚,一个是检验真理,一个是研究真理
你们都是老大,我不行。
其实都是高手啊。


每个人都顾及自己的面子,中国不要搞研究了。


mark


狂水的一个贴,在不结没完叻


ajoo: 看你,非说偏心吧, 闫博士不来了。 ^_^
开玩笑呢。 DbC与lsp的关系您现在想法是什么?


别吵了!赶快揭帖!
告诉你们用笛卡儿乘积集来描述类,你就知道到底是正方形继承长方形,还是马继承黑马!!
posted on 2007-09-20 12:42 teasp 阅读(2133) 评论(2)  编辑  收藏 所属分类: Java学习

FeedBack:
# re: 白马是马吗? 2007-09-22 06:08 白马
扩展不就是特殊情况吗?  回复  更多评论
  
# re: 白马是马吗? 2007-09-22 09:23 Matthew Chen
楼上说的在理,原帖是4年前的吧,那时java方兴未艾,完全oo的想法正流行,大家对oo也都还带有不同程度的误解,而今再回首,也许都看得开了。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问