問(wèn)題是什么
白馬是馬嗎?本來(lái)這不應(yīng)該是個(gè)問(wèn)題,可是到了OO程序員這里,它就成了問(wèn)題。不過(guò)程序員們通常討論的是正方形是否是長(zhǎng)方形,這和白馬是否是馬是同一個(gè)問(wèn)題。
問(wèn)題的起源
這就得先說(shuō)面向?qū)ο罅?,面向?qū)ο缶幊逃幸粋€(gè)重要原則是里氏替換原則(LSP,Liskov Substitution Principle)。這個(gè)原則很簡(jiǎn)單,就是凡是用父類(lèi)的地方就可以用子類(lèi)替換之。形象點(diǎn)就是,老子做得,兒子就做得。可是在程序世界里往往能產(chǎn)生讓人困惑的結(jié)局。最經(jīng)典的就是正方形是否應(yīng)該繼承長(zhǎng)方形的問(wèn)題,由于下面將引用別人的討論,我在這里就不寫(xiě)具體的代碼了。父類(lèi)長(zhǎng)方形有長(zhǎng)和寬兩個(gè)屬性,有計(jì)算面積的方法:長(zhǎng)*寬。子類(lèi)正方形可以繼承這些,可是它要保證長(zhǎng)=寬,于是它在設(shè)置寬或長(zhǎng)的時(shí)候同時(shí)修改長(zhǎng)或?qū)捯源_保長(zhǎng)=寬?,F(xiàn)在我把一個(gè)正方形的實(shí)例a當(dāng)長(zhǎng)方形用,給a設(shè)置長(zhǎng)是4寬是5,那么計(jì)算出來(lái)的面積會(huì)是多少呢?答案是令人驚訝的25而不是應(yīng)該的20。這就不符合LSP了。所以,問(wèn)題產(chǎn)生了,正方形是長(zhǎng)方形嗎?
個(gè)人觀點(diǎn)
正方形是長(zhǎng)方形。不過(guò)面向?qū)ο罄锏睦^承關(guān)系在現(xiàn)實(shí)中并不存在。現(xiàn)實(shí)中的父類(lèi)和子類(lèi)并不遵循LSP,比如馬可以把毛染黑,而白馬就不行,因?yàn)榘遵R染黑了就不是白馬了,這就不符合LSP。實(shí)際上,現(xiàn)實(shí)生活中的子類(lèi)是父類(lèi)的特殊情況(子集),而OO里面子類(lèi)是對(duì)父類(lèi)的擴(kuò)展。而且程序里對(duì)某個(gè)類(lèi)的定義常常和現(xiàn)實(shí)中不一樣,比如我們定義的長(zhǎng)方形真的能?chē)?yán)格對(duì)應(yīng)現(xiàn)實(shí)中的長(zhǎng)方形嗎?也就是說(shuō)此長(zhǎng)方形非彼長(zhǎng)方形,那么開(kāi)頭的那個(gè)問(wèn)題其實(shí)是人們自己給自己找煩惱。現(xiàn)實(shí)中,老子做得兒子不一定做得,但是OO卻要求老子做得兒子就一定做得(當(dāng)然你也可以不遵守)。
下面是轉(zhuǎn)來(lái)的別人的討論,非常精彩:
昨天看了閻宏博士寫(xiě)的<<Java與模式>>一書(shū)中講到里氏代換的內(nèi)容時(shí),其中提到了長(zhǎng)方形是不是正方形父類(lèi)的問(wèn)題(第七章 Page 77)我第一次看了他的例子,感覺(jué)是那么回事,
函數(shù)內(nèi)容如下:
public void resize(Rectangle r){
while(r.getHeight()<r.getWidth()){
r.setWidth(r.getWidth()+1);
}
}
書(shū)中說(shuō)道,如果客戶(hù)端使用一個(gè)Square(正方形)對(duì)象調(diào)用這個(gè)函數(shù),就會(huì)得出與使用Retangle對(duì)象不通的結(jié)論。當(dāng)Rectangle對(duì)象時(shí),resize()方法會(huì)將寬度不斷增加,直到超過(guò)長(zhǎng)度才停下來(lái),如果傳入的是一個(gè)Square對(duì)象時(shí),這個(gè)resize方法就會(huì)將正方形的邊不斷增加下去,直到溢出。
不過(guò)我馬上就產(chǎn)生了一個(gè)疑惑,那個(gè)resize函數(shù)也太特殊了。
如果我也定義一個(gè)函數(shù)
其中限制if height = 100 return ;那么高度為100的長(zhǎng)方形和長(zhǎng)方形類(lèi)也不符合里氏代換,豈不是也要定義成一個(gè)單獨(dú)的類(lèi)?而不能作為長(zhǎng)方形的子類(lèi),依此類(lèi)推。。。。。我真的蒙了。
我始終有一個(gè)直覺(jué),正方形就是長(zhǎng)方形的一種特殊形式,應(yīng)該定義為其子類(lèi),希望高手們能給我一個(gè)確切答案。
個(gè)人感覺(jué)正方形的確不是長(zhǎng)方形的子類(lèi),它是在長(zhǎng)方形上多了一個(gè)條件“長(zhǎng) == 寬”。所以,長(zhǎng)方形適合的地方,正方形不一定適合。
如果一定要把長(zhǎng)方形和正方形拉上關(guān)系,我認(rèn)為正方形應(yīng)該是通過(guò)長(zhǎng)方形來(lái)實(shí)現(xiàn)的。只是在正方形的setter方法里面加上一定的控制,讓它里面的長(zhǎng)方形的長(zhǎng)和寬相同。
呵呵,昨天才看到這一章,這是我的理解,不知道斑竹大人和各位高手怎么看^_^
to binny(騎個(gè)破車(chē)看夕陽(yáng)) “個(gè)人感覺(jué)正方形的確不是長(zhǎng)方形的子類(lèi),它是在長(zhǎng)方形上多了一個(gè)條件“長(zhǎng) == 寬”。所以,長(zhǎng)方形適合的地方,正方形不一定適合。”
沒(méi)錯(cuò)啊,正方形只不過(guò)是長(zhǎng)方形的一種,長(zhǎng)方形->長(zhǎng)寬比例固定的長(zhǎng)方形->長(zhǎng)寬比例固定而且長(zhǎng)=寬的長(zhǎng)方形,,這樣不行嗎?
或者就是在resize中將有限的特殊情況考慮進(jìn)去,不然,把什么都特殊化,那也就失去特殊的意義了。
to binny(騎個(gè)破車(chē)看夕陽(yáng)) “個(gè)人感覺(jué)正方形的確不是長(zhǎng)方形的子類(lèi),它是在長(zhǎng)方形上多了一個(gè)條件“長(zhǎng) == 寬”。所以,長(zhǎng)方形適合的地方,正方形不一定適合。”
如果我也定義一個(gè)函數(shù)
其中限制if height = 100 return ;那么高度為100的長(zhǎng)方形也成了一個(gè)特例了,其他長(zhǎng)方形都適合,“這種特殊長(zhǎng)方形”就不適合了,怎么辦?if height =200,=300.....5000呢?又怎么辦?
說(shuō)得有道理,其實(shí),后面寫(xiě)到的的四邊形與長(zhǎng)方形的關(guān)系,包括馬與黑馬的關(guān)系,都不是繼承關(guān)系(雖然書(shū)上寫(xiě)的是繼承)。
很顯然,如果我讓四邊形的一個(gè)內(nèi)角增大到比另外一個(gè)內(nèi)角要大,無(wú)論長(zhǎng)方形和正方形都不符合。
同樣,馬科已被染上各種顏色,但是黑馬不可以,她如果被染上了除了黑色以外的顏色,它就不是黑馬了。
實(shí)際上,oo中的繼承指的是功能上的繼承,而不是條件上的繼承。也就是說(shuō),子類(lèi)應(yīng)該有父類(lèi)所擁有的功能,并可能有所發(fā)展,而不是在條件上面有所發(fā)展。這樣才能符合理氏代換,否則,條件都增加了,怎么能替換呢,這樣只能是子集,而不是子類(lèi)。
do not take it toooooo serious
一點(diǎn)看法:
組合關(guān)系是客觀存在的,繼承關(guān)系并非客觀存在的,推薦盡量少用繼承。
很抱歉,書(shū)中有一處打印錯(cuò)誤
public void resize(Rectangle r){
while(r.getHeight()<r.getWidth()){
r.setWidth(r.getWidth()+1);
}
}
應(yīng)當(dāng)是
public void resize(Rectangle r){
while(r.getWidth()<r.getHeight()){
r.setWidth(r.getWidth()+1);
}
}
由于篇幅限制,在我的書(shū)中并沒(méi)有引入Design by Contract的概念。子類(lèi)應(yīng)當(dāng)完全繼承父類(lèi)的contract。
上面的檢測(cè)條件在Rectangle類(lèi)的contract里面,但是不在Square類(lèi)的contract里面。這就是錯(cuò)誤的。
反過(guò)來(lái),樓上的朋友所提到的width == 100這個(gè)條件并不在Rectangle的contract里面,因此Square不必繼承它,當(dāng)然也就談不上違反。
如果你一定要把width == 100這個(gè)條件放到里面Rectangle的contract里面,那么Rectangle就變成了(比如)邊長(zhǎng)不等于100的長(zhǎng)方形,而子類(lèi)是一個(gè)邊長(zhǎng)等于100的長(zhǎng)方形。那么當(dāng)然子類(lèi)違反了LSP。這個(gè)時(shí)候你就只好把邊長(zhǎng)等于100的長(zhǎng)方形和邊長(zhǎng)不等于100的長(zhǎng)方形并列起來(lái),并為它們選擇一個(gè)共同的子類(lèi)。
并為它們選擇一個(gè)共同的父類(lèi)。
(還沒(méi)睡醒)
OOSE書(shū)中均有解釋?zhuān)^承分成多種,具體我不太記得清,大概10種左右吧,每一種有其適用范圍,這種屬于父類(lèi)輸入條件對(duì)子類(lèi)不適用的一類(lèi),當(dāng)然可以這么做,但是會(huì)有各位提到的缺陷,真正適合繼承的種類(lèi)非常少,所以說(shuō)少用繼承。
長(zhǎng)方形有二個(gè)屬性長(zhǎng)和寬。并有一個(gè)設(shè)置長(zhǎng)的方法和設(shè)置寬的方法,還有一個(gè)求面積的方法.
像下面
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;
}
如果說(shuō)正方形是長(zhǎng)方形的子類(lèi)。為了保證正方形長(zhǎng)和寬相等,那對(duì)應(yīng)于正方形的二設(shè)置長(zhǎng)寬的個(gè)方法就得改成
public void setLength(int lenght) {
this.length = lenght;
this.width= lenght;
}
public void setWidth(int width) {
this.length = width;
this.width= width;
}
那我們想想用戶(hù)使用時(shí)候的情景。 我們都知道長(zhǎng)方形的面積等于長(zhǎng)與寬的積。那當(dāng)我們用長(zhǎng)方形的時(shí)候我們會(huì)這樣用
Rectangle rectangle = new Rectangle();
rectangle.setLength(5);
rectangle.setWidth(4);
我們想知道面積是多少我們就可以
rectangle.getArea();
得到的是20,當(dāng)然結(jié)果是非常正確的。
但想想如果我們把一個(gè)正方形的實(shí)例給用戶(hù)用的時(shí)候會(huì)怎么樣
Rectangle rectangle = new Square(); //注意,這里體顯代換原則。用戶(hù)根本不知道真正的實(shí)例是正方形,用戶(hù)只知道長(zhǎng)方形的事情。
rectangle.setLength(5);
rectangle.setWidth(4);
我們想知道面積是多少我們就可以
rectangle.getArea();
得到的結(jié)果卻是 16 ,這違背了長(zhǎng)方形的面積是長(zhǎng)與寬之積的原則。用戶(hù)就不會(huì)明白為什么我設(shè)置了長(zhǎng)是5寬是4得到的答案卻是16 ?? 與前提不符
所以正方形不能代替長(zhǎng)方形出現(xiàn)在這個(gè)地方。
也就是說(shuō)正方形不應(yīng)當(dāng)看作是長(zhǎng)方形的子類(lèi)。
to:jeffyan77(jeffyan77) 是閻博士本人?首先,非常感謝能夠得到你親自指點(diǎn)。你的書(shū)文筆很好,實(shí)例多多,由淺入深,我去年就翻了一下,印象很深刻,不過(guò)當(dāng)時(shí)沒(méi)有做JAVA,所以沒(méi)有仔細(xì)研究,現(xiàn)在正好要用JAVA做項(xiàng)目,于是下狠心買(mǎi)了一本您的書(shū),打算慢慢學(xué)習(xí)。希望能有更多想你這樣的書(shū)出現(xiàn)在市面上。
就這個(gè)話(huà)題,您提到“Design by Contract”又是什么理論?在哪里可以找到相關(guān)資料?如果內(nèi)容不多,能否在這里多說(shuō)幾句?
順便說(shuō)一下,飛思網(wǎng)站最近無(wú)法注冊(cè),不能進(jìn)入你的專(zhuān)欄發(fā)貼,所以我跑到這里來(lái)了,沒(méi)想到還真碰上了你。
據(jù)我所知孟巖好像有一本譯作是關(guān)于DbC(Design by Contract)的,馬上就要出版了。建議到www.china-pub.com看看。
所謂Contract,就是指一個(gè)方法的前條件、后條件加上不變量。所有的繼承都應(yīng)當(dāng)是Contract的繼承。一個(gè)簡(jiǎn)單的介紹可見(jiàn)
http://www.gauss.muc.de/tools/dbc/dbc-intro.html
Eiffel語(yǔ)言直接支持DbC。如果你使用Java的話(huà),可以考慮安裝iContract,那樣可以在Visual Age for Java里面直接聲明Contract。
我的意見(jiàn),如果僅是應(yīng)用的話(huà),理解DbC的含義就可以了,除非對(duì)DbC理論有特別興趣,不然不必要讀專(zhuān)著。
飛思網(wǎng)站的問(wèn)題我會(huì)和網(wǎng)管講,謝謝提醒。
truezerg(趙明宇) ,
1.你這個(gè)例子的確能說(shuō)明一定問(wèn)題,不過(guò)這并不是我的疑惑產(chǎn)生的根源。
概括來(lái)說(shuō),我的主要疑惑在于對(duì)特殊化的程度。從你這個(gè)例子看來(lái),長(zhǎng)寬比例固定的長(zhǎng)方形此時(shí)的地位就和正方形相似了。其實(shí)每一個(gè)長(zhǎng)寬不同的長(zhǎng)方形,就細(xì)了說(shuō),都可以說(shuō)是特殊的,只不過(guò)我們常常用到正方形這種特殊形狀。那么,就這些“相對(duì)有限”的特殊形狀,能不能在長(zhǎng)方形的相應(yīng)屬性中設(shè)置標(biāo)志,比如叫boolean whFixed 構(gòu)造器中是whFixed = false .在setWidth和setHeight中就判斷這個(gè)值的真假做相應(yīng)的處理。Square類(lèi)可以有一個(gè)構(gòu)造器,內(nèi)容是whFixed = true;
2.你所說(shuō)的。。。。。“但想想如果我們把一個(gè)正方形的實(shí)例給用戶(hù)用的時(shí)候會(huì)怎么樣
Rectangle rectangle = new Square(); //注意,這里體顯代換原則。用戶(hù)根本不知道真正的實(shí)例是正方形,用戶(hù)只知道長(zhǎng)方形的事情”。。。。。。。。。。。
結(jié)果面積得出的是16而不是20。我認(rèn)為這是很正常的事情,那只能說(shuō)用戶(hù)對(duì)正方形的特性還不完全了解。作為子類(lèi),肯定有和父類(lèi)不同的地方。包括方法的重寫(xiě)等,也是常用的。另外,我記得有個(gè)著名的以以Shape和其他形狀的calcArea來(lái)說(shuō)明多態(tài)的例子:許多不明形狀的對(duì)象放在一個(gè)數(shù)組中,然后用(Shape) arrShape[i].calcArea,能夠得到相應(yīng)對(duì)象正確的面積。也就是說(shuō),你的那個(gè)例子,本來(lái)就應(yīng)該這樣。
結(jié)果面積得出的是16而不是20。我認(rèn)為這是很正常的事情,那只能說(shuō)用戶(hù)對(duì)正方形的特性還不完全了解。作為子類(lèi),肯定有和父類(lèi)不同的地方。包括方法的重寫(xiě)等,也是常用的。
------------------------------------------------------------------------------
你所說(shuō)的用戶(hù)對(duì)正方形的特性還不過(guò)完全了解。這句話(huà)不對(duì)。 首先如果把正方形當(dāng)成長(zhǎng)正形的子類(lèi)的話(huà),用戶(hù)沒(méi)有義務(wù)去了解正方形。因?yàn)樗鼈兪悄盟?dāng)長(zhǎng)方形來(lái)用的。你不可能讓用戶(hù)去了解所有的子類(lèi)。如果每個(gè)子類(lèi)用戶(hù)都必需要了解它們的特性,必需要撐握如何使用它,那就失去了多態(tài)性了。因?yàn)槊慨?dāng)使用一個(gè)新的子類(lèi)去換掉父類(lèi)就得對(duì)這個(gè)子類(lèi)的特別情況加以處理,這非常不合理。
方法重寫(xiě)的問(wèn)題:方法可以重寫(xiě),但要符合父類(lèi)限定的條件。在這里,長(zhǎng)方形求面積的方法前提規(guī)定的是長(zhǎng)與寬的積。如果你重寫(xiě)了這個(gè)方法但沒(méi)有符合這個(gè)前提,程序倒是可以編譯通過(guò),但不符合代替原則。所以出現(xiàn)這種情況有種暗示你的類(lèi)結(jié)構(gòu)有點(diǎn)問(wèn)題。(當(dāng)然如果你改變了前提條件的話(huà)或許有可能,比如改變長(zhǎng)方形求面積的規(guī)則)
Shape類(lèi)的問(wèn)題:
對(duì)于長(zhǎng)方形繼承Shape類(lèi)的這種情況,和正方形繼承長(zhǎng)方形有所不同。 因?yàn)镾hape類(lèi)沒(méi)有一個(gè)求面積的方法(沒(méi)有實(shí)現(xiàn))。所以就沒(méi)有一個(gè)前提規(guī)定該如何求。所以長(zhǎng)方形當(dāng)然可以自己實(shí)現(xiàn)如何求。沒(méi)有任何限制。但正方形繼承長(zhǎng)方形以后就受到長(zhǎng)方形的限制了。
另外,如果我們找到了Shape類(lèi)的統(tǒng)一的求面積的方法。比如用微積分來(lái)求。那長(zhǎng)方形繼承Shape也是正確的。 因?yàn)橛梦⒎e分的方法來(lái)得到長(zhǎng)方形的面積,得到的也是一個(gè)正確的結(jié)果。長(zhǎng)方形并沒(méi)有違反父類(lèi)的規(guī)則,同時(shí)任何圖形都可以通過(guò)微積分來(lái)求得面積,而且都是正確的,所以用戶(hù)根本不用考慮給我的是什么圖形,根本不用考慮這個(gè)圖形的特殊點(diǎn)。只要通過(guò)父類(lèi)的求面積的方法就可以得到正確的結(jié)果。
我覺(jué)得無(wú)論正方型作為長(zhǎng)方形的子類(lèi)或者長(zhǎng)方形作為正方形的子類(lèi)都有不妥的地方。它們有很多共同的地方也有很多不同的地方。可以設(shè)計(jì)一個(gè)抽象的“矩形”類(lèi),長(zhǎng)方形和正方型都繼承它。所有長(zhǎng)方形和正方形的共同特性在父類(lèi)“矩形類(lèi)”中實(shí)現(xiàn),所有不同的特性在子類(lèi)中實(shí)現(xiàn)。這樣不會(huì)影響長(zhǎng)方形和正方型的內(nèi)部實(shí)現(xiàn)邏輯,而且可以保證最大程度的功能復(fù)用。
我覺(jué)得還是把長(zhǎng)方形看成是類(lèi),正方形看成是長(zhǎng)方形類(lèi)的一個(gè)實(shí)例比較合適。
閻博士你好,沒(méi)想到能遇見(jiàn)你,很高興,同時(shí)想請(qǐng)教關(guān)于您書(shū)上的一個(gè)問(wèn)題。和上面的兄弟一樣,我也覺(jué)得黑馬并非是馬的一個(gè)子類(lèi)。當(dāng)然,黑馬“是”馬沒(méi)有錯(cuò)誤,我只是覺(jué)得所謂“黑”只是馬的一個(gè)屬性,所有馬都有顏色這個(gè)屬性。黑或白只是屬性的值不同。 不知道為什么黑馬和白馬要成為馬的子類(lèi)。這樣做有什么有什么實(shí)踐價(jià)值?
麻煩了閻博士,請(qǐng)您再說(shuō)明白一下。謝
所有的繼承關(guān)系都可以改為委派關(guān)系,所有的is-a關(guān)系都可以改為has-a關(guān)系。
譬如在某些系統(tǒng)中可以認(rèn)為Person是超類(lèi)型,Man和Woman是子類(lèi)型,這是繼承關(guān)系,這個(gè)提法在很多著作中都當(dāng)作例子出現(xiàn),比如Peter Coad的書(shū)。你當(dāng)然也可以把性別拿出來(lái),變成Person的屬性,這樣就成了has-a關(guān)系。(這種做法是State模式或者Strategy模式。)
你可以把工作中的繼承關(guān)系拿出來(lái),按照這個(gè)思路試一試。
OO技術(shù)是一個(gè)強(qiáng)大的工具,幾乎每一個(gè)問(wèn)題都可以由多個(gè)解決方案。
PeterChen所說(shuō)的“可以設(shè)計(jì)一個(gè)抽象的“矩形”類(lèi),長(zhǎng)方形和正方型都繼承它”最接近我的意見(jiàn)。
大家討論。
我覺(jué)得正方形完全沒(méi)有必要當(dāng)做一個(gè)類(lèi)來(lái)出現(xiàn)。它就是長(zhǎng)方形的一個(gè)實(shí)例,只不過(guò)它是一個(gè)長(zhǎng)和寬相等長(zhǎng)方形。
當(dāng)然設(shè)計(jì)一個(gè)抽象的矩形類(lèi),然后長(zhǎng)方形和正方形都繼承它也沒(méi)錯(cuò)。
什么樣的設(shè)計(jì)取決于什么樣的使用環(huán)境。 設(shè)計(jì)和使用環(huán)境是分不開(kāi)的
我覺(jué)得問(wèn)題在于繼承一個(gè)具體類(lèi)上。
Square繼承的是Rectangle這個(gè)具體類(lèi),而這個(gè)具體類(lèi)又實(shí)現(xiàn)了Area這個(gè)方法。那么子類(lèi)的Area要符合父類(lèi)的限定條件。
而如果設(shè)計(jì)一個(gè)抽象的矩形類(lèi),則沒(méi)有對(duì)Area方法有具體的實(shí)現(xiàn),所以子類(lèi)可以自己實(shí)現(xiàn)。
假設(shè)我們?cè)O(shè)計(jì)一個(gè)具體的四邊形的類(lèi),定義了改變角度的方法,那么長(zhǎng)方形是不是也不應(yīng)當(dāng)去繼承?
to:truezerg(趙明宇)
...........................
我覺(jué)得正方形完全沒(méi)有必要當(dāng)做一個(gè)類(lèi)來(lái)出現(xiàn)。它就是長(zhǎng)方形的一個(gè)實(shí)例,只不過(guò)它是一個(gè)長(zhǎng)和寬相等長(zhǎng)方形。
當(dāng)然設(shè)計(jì)一個(gè)抽象的矩形類(lèi),然后長(zhǎng)方形和正方形都繼承它也沒(méi)錯(cuò)。
什么樣的設(shè)計(jì)取決于什么樣的使用環(huán)境。 設(shè)計(jì)和使用環(huán)境是分不開(kāi)的
...........................
這幾句話(huà),我基本贊同
看了閻博士的解釋豁然開(kāi)朗,呵呵,受教了。
樓上的,我想你的疑惑和我前天的想法是一樣的
但是現(xiàn)在,我不會(huì)在一個(gè)四邊形的類(lèi)里面寫(xiě)改變角度的方法,也不會(huì)寫(xiě)改變邊長(zhǎng)比例的方法,
因?yàn)椴皇撬械乃倪呅味伎梢杂羞@兩種改變,
所以抽象出來(lái)的四邊形也不應(yīng)該有這兩種改變。
如果業(yè)務(wù)要求作這兩種改變的話(huà),
那么它操作的對(duì)象應(yīng)該不是四邊形,而是除了長(zhǎng)方形以外的四邊形。
TO:truezerg(趙明宇)
感覺(jué)這個(gè)問(wèn)題上我們的理解有許多共同的地方
而且在許多貼子里面看到你的發(fā)言,感覺(jué)你對(duì)java的理解非常透徹
把你的MSN給我好嗎,想和你好好學(xué)習(xí)一下,呵呵
設(shè)計(jì)是要看目標(biāo)的,如果我們?cè)O(shè)計(jì)的正方形和長(zhǎng)方形在真正的企業(yè)級(jí)應(yīng)用中那就要考慮擴(kuò)展性和穩(wěn)定性等因素了。
正方形現(xiàn)在的和長(zhǎng)方形唯一不同的是width==height,但是在真實(shí)環(huán)境中要考慮將來(lái)正方型和長(zhǎng)方形都有可能發(fā)生變化,它們有可能有更多不一樣的地方。如果變化了我們的設(shè)計(jì)是否需要改動(dòng)?如果是這樣我還是覺(jué)得“設(shè)計(jì)一個(gè)抽象的矩形類(lèi),然后長(zhǎng)方形和正方形都繼承它”的方法好一些。因?yàn)檫@種方法可以有更好的可擴(kuò)展性和靈活性。
而如果僅從單純的教學(xué)例子的角度出發(fā)我還是比較贊同“正方形是長(zhǎng)方形的一個(gè)實(shí)例”的。
所以正如truezerg(趙明宇) 所說(shuō):“什么樣的設(shè)計(jì)取決于什么樣的使用環(huán)境”
breakpoint_fish@hotmail.com
我的這個(gè)MSN里面基本上全都是程序員。都是討論問(wèn)題的,互相學(xué)習(xí)而已。
不好為什么msn上不去了,郁悶
長(zhǎng)方形的學(xué)名應(yīng)該是矩形,矩形的定義(對(duì)邊相等,四個(gè)角為直角的四邊形或有一個(gè)直角的平行四邊形)中并沒(méi)有“長(zhǎng)不等于寬”,從這個(gè)定義來(lái)說(shuō),我覺(jué)得似乎沒(méi)有理由認(rèn)為正方形不是長(zhǎng)方形的子類(lèi);另外我覺(jué)得resize()的定義也有問(wèn)題,這只是普通矩形即非正方形的行為;子類(lèi)也可以重載父類(lèi)的行為(方法),否則重載又有何意義?
如果說(shuō)上面的例子中所指的長(zhǎng)方形是普通長(zhǎng)方形(即長(zhǎng)寬不等的),那么這個(gè)問(wèn)題就沒(méi)有討論的必要了,在現(xiàn)實(shí)中它們也不是父類(lèi)和子類(lèi)的關(guān)系。
可見(jiàn)這個(gè)例子實(shí)在不是個(gè)很好的例子。
是的,上面的“矩形”說(shuō)法不夠嚴(yán)謹(jǐn)。實(shí)際上我記得我的書(shū)中說(shuō)的是抽象“四邊形(Quadrangle)”類(lèi),長(zhǎng)方形Rectangle類(lèi)和正方型Square類(lèi)都繼承它。
長(zhǎng)方形的Contract是width和height可以獨(dú)立變化,這個(gè)contract在正方形中被破壞了。為什么說(shuō)長(zhǎng)方形的Contract包含了獨(dú)立變化這一條呢?你看看長(zhǎng)方形的接口就會(huì)發(fā)現(xiàn),width和height可以獨(dú)立設(shè)定,這就是說(shuō)二者可以獨(dú)立變化。而在給出正方形時(shí),即便接口不變,也要千方百計(jì)地破壞這個(gè)contract,因?yàn)檎叫尾豢赡苡袃蓚€(gè)邊長(zhǎng)。
所以從Contract繼承的角度上講,Rectangle和Square不可能有繼承關(guān)系,它們合并起來(lái),可以給出一個(gè)更大的Contract,這就是一個(gè)抽象超類(lèi)。
這個(gè)長(zhǎng)方形和正方形的例子不是我發(fā)明的,這是至少流行了十年的思辨題目,最早來(lái)自于C++和Smalltalk領(lǐng)域。我把它搬到了Java領(lǐng)域,問(wèn)題和答案都沒(méi)有改變。
這個(gè)問(wèn)題之所以有意義,就是因?yàn)樗f(shuō)明繼承的概念與日常生活中的概念或者數(shù)學(xué)概念都是有區(qū)別的,在數(shù)學(xué)上長(zhǎng)方形當(dāng)然包括正方形,但是在OO設(shè)計(jì)中除非你將二者設(shè)計(jì)成Contract繼承關(guān)系,不然它們就不是繼承關(guān)系。
當(dāng)我們說(shuō)到Rectangle是不是Square的超類(lèi)時(shí),我們所說(shuō)的不是數(shù)學(xué)上的Rectangle和Square,我們說(shuō)的是一個(gè)定義好的Rectangle類(lèi),和一個(gè)不知道如何定義的Square子類(lèi)。結(jié)果我們發(fā)現(xiàn)這個(gè)Square最好獨(dú)立定義,而不是作為Rectangle的子類(lèi)定義。
大家討論。
看了敏捷軟件開(kāi)發(fā),里面提到這個(gè)問(wèn)題說(shuō):
DBC對(duì)Rectangle的SetWidth這個(gè)方法的前置條件是this.Width == newWidth and this.Height == oldHeight,很明顯Square的SetWidth不滿(mǎn)足Rectangle的Contract,所以不應(yīng)當(dāng)設(shè)計(jì)成繼承關(guān)系
>>長(zhǎng)方形的Contract是width和height可以獨(dú)立變化,這個(gè)contract在正方形中被破壞了。
我覺(jué)得這有偷換概念的嫌疑,width和height可以獨(dú)立變化本身就是長(zhǎng)方形中的某些“特例”(雖然從比例上說(shuō)幾乎是全部長(zhǎng)方形),并不能用作長(zhǎng)方形的Contract。如果可以,那么以此類(lèi)推,四邊形的四角大小也是可以變化的,只要滿(mǎn)足360度總和的條件,但長(zhǎng)方形顯然四個(gè)角大小是不能變化的,由此豈非可以得出類(lèi)似結(jié)論:長(zhǎng)方形也不能作為四邊形的子類(lèi)?
to tripofdream(夢(mèng)之旅)
Square違反Rectangle的Contract,是站在SetWidth,SetHeight這個(gè)角度來(lái)看的。
如果你從改變四邊形角度的方法看,那長(zhǎng)方形與四邊形也應(yīng)該不能使用繼承關(guān)系。但如果從其他角度看,依然是繼承關(guān)系
要清楚子類(lèi)是對(duì)父類(lèi)的擴(kuò)展,而不是對(duì)父類(lèi)的“約束”,所以如果四邊形里定義的方法或性質(zhì)無(wú)法讓子類(lèi)(比如長(zhǎng)方形)來(lái)擴(kuò)展,反而卻因?yàn)樽宇?lèi)的存在約束了父類(lèi)的行為。那就是錯(cuò)了。
所以長(zhǎng)方形能不能做四邊形的子類(lèi)關(guān)鍵看四邊形的定義。
還是那句話(huà),設(shè)計(jì)取決于你的應(yīng)用環(huán)境
有種真理越辯越明的感覺(jué)
再來(lái)談?wù)勎业南敕?br />
首先,繼承關(guān)系是人自己想象出來(lái)的,并不客觀存在。是人們用來(lái)實(shí)現(xiàn)復(fù)用和多態(tài)的一個(gè)手段。
所以脫離代碼討論是否是繼承關(guān)系是空洞的。
其次,既然是用來(lái)實(shí)現(xiàn)復(fù)用和多態(tài)的,那么,想定義某兩個(gè)事物之間的關(guān)系是繼承關(guān)系,
要看他們是不是有利于復(fù)用和多態(tài),而不是在我們定義中符合什么什么關(guān)系
總之,我們不是為了繼承而繼承。所以試著不要直接就認(rèn)為他們是繼承關(guān)系,
然后找他們不是繼承的原因。反過(guò)來(lái)試試是找理由說(shuō)服自己它為什么是繼承關(guān)系。
tripofdream(夢(mèng)之旅) :
width和height可獨(dú)立變化不是長(zhǎng)方形的特例,這是存在于Rectangle類(lèi)代碼中的。注意我們討論的并不是數(shù)學(xué)的長(zhǎng)方形,而是叫做Rectangle的一個(gè)類(lèi)。
只要width和height失去獨(dú)立變化的能力,不管它們是相等還是形成一個(gè)比例,都不能成為Rectangle的子類(lèi)。
譬如width = N * height的條件一旦加到類(lèi)上,那么這個(gè)類(lèi)就不再是Rectangle的子類(lèi)。N=1的時(shí)候就是我們所討論的正方形。
說(shuō)的更加廣泛一點(diǎn),只要width和height之間出現(xiàn)約束,譬如width = f( height ),那就不再是Rectangle的子類(lèi)了。
正方型應(yīng)該是矩形的子類(lèi),類(lèi)的關(guān)系不能以太生活化的邏輯評(píng)定。最抽象最有共性的當(dāng)然應(yīng)該是父類(lèi),逐級(jí)分類(lèi)。
樓上的,那是抽象與個(gè)體的關(guān)系,是類(lèi)與實(shí)例的關(guān)系。
沒(méi)人說(shuō)過(guò)“最抽象最有共性的當(dāng)然應(yīng)該是父類(lèi)”這樣的話(huà)呀
是不是繼承關(guān)系,要看你的具體設(shè)計(jì)是什么樣的,
其實(shí)長(zhǎng)方形正方形可以設(shè)計(jì)成繼承關(guān)系,也可以不是,
因?yàn)槔^承是語(yǔ)法上的東西,你寫(xiě)上extends他就是繼承
但是設(shè)計(jì)成繼承關(guān)系是不是合適,那就是另一回事了。
我們現(xiàn)在討論的就是把他們?cè)O(shè)計(jì)成繼承關(guān)系是不是合適的問(wèn)題。
對(duì)!樓上說(shuō)的好,設(shè)計(jì)模式就是讓我們不要亂繼承~
例如:植物類(lèi)有生長(zhǎng)方法,一塊磚頭為了生長(zhǎng)不應(yīng)繼承植物類(lèi),雖然你可以extends也沒(méi)錯(cuò)!
但不和理。
暈,能有這么解釋的嗎?
到底正方形是不是長(zhǎng)方形的子類(lèi)取決于你抽象的程度和抽象的語(yǔ)義。
如果你定義“長(zhǎng)方形”(Rectangle)這一類(lèi)對(duì)象的固有特征是有兩條互相垂直的邊( Rectangle.width ,Rectangle.length),能夠計(jì)算出面積(Rectangle.getArea()),
定義菱形為(Diamond)擁有四個(gè)相等的邊(Diamond.side)的實(shí)體,能夠計(jì)算出面積(Diamond.getArea())
毫無(wú)疑問(wèn),這在c++里面不是問(wèn)題,因?yàn)樗С侄嘀乩^承,Square :Rangle,Diamond.
但是java不支持,于是這就成了一個(gè)問(wèn)題,到底哪個(gè)是父類(lèi),哪個(gè)該作為一個(gè)接口實(shí)現(xiàn)?
但是確確實(shí)實(shí),Square是一個(gè)子類(lèi),而不是Rangle和Diamond的聚合。
上面有人提到了當(dāng) Square.setWidth(5),Square.setHeight(4)以后,為什么最后getArea()==16而不是20,這和父類(lèi)沒(méi)有關(guān)系,也不需要你去理解父類(lèi),而是你的特殊子類(lèi)修改了父類(lèi)的實(shí)現(xiàn)細(xì)節(jié)。當(dāng)你的Square.setHeight(4)的時(shí)候,你應(yīng)該以某種方式(比如異常)告訴調(diào)用者,他的行為已經(jīng)隱含的修改了Square.width——你的setHeight()已經(jīng)修改了父類(lèi)的方法!??!
抽象不是空中樓閣,不是哲學(xué)上討論白馬非馬的問(wèn)題,而是為了讓你的設(shè)計(jì)更清晰的體現(xiàn)現(xiàn)實(shí)生活中的對(duì)象實(shí)體,如果沒(méi)有現(xiàn)實(shí)條件的依托,你的抽象還有什么意味?(說(shuō)句不中聽(tīng)的話(huà),與其看一些夸夸其談的東西,不如回頭看看哲學(xué))
在一本書(shū)看到的一個(gè)觀點(diǎn)是:如果你的抽象繼承的層次超過(guò)了5層,你應(yīng)該回頭檢查一下你的設(shè)計(jì)了。這個(gè)原則適合于任何設(shè)計(jì)。
我強(qiáng)烈贊同 asdmonster(asd)的言論!這才是我要的結(jié)論!
當(dāng)然,這是我的看法,本貼再留一留,大家也討論了這么些天,估計(jì)不同意見(jiàn)也不會(huì)太多了。
當(dāng)你的Square.setHeight(4)的時(shí)候,你應(yīng)該以某種方式(比如異常)告訴調(diào)用者,他的行為已經(jīng)隱含的修改了Square.width——你的setHeight()已經(jīng)修改了父類(lèi)的方法?。?!
-----------------------------------------------------------------
姑且不談?dòng)卯惓5姆绞礁嬖V調(diào)用者在設(shè)置長(zhǎng)的時(shí)候也設(shè)置了寬這件事情好不好。 從沒(méi)有見(jiàn)過(guò)這樣用異常的。
但是不管你要不要告訴調(diào)用者長(zhǎng)與寬同時(shí)被改變了,這都是違反了長(zhǎng)方形(也就是父類(lèi))的規(guī)則。用戶(hù)就不明白為什么我設(shè)置一個(gè)長(zhǎng)方形(因?yàn)橛脩?hù)不知道實(shí)例是個(gè)正方形)的長(zhǎng)或?qū)捒偸前l(fā)生一個(gè)異常?? 這比什么也不告訴用戶(hù)而是得到面積為16更加不可思議。
TO:asdmonster(asd)
>當(dāng)你的Square.setHeight(4)的時(shí)候,你應(yīng)該以某種方式(比如異常)告訴調(diào)用者,他的行為已經(jīng)隱>含的修改了Square.width——你的setHeight()已經(jīng)修改了父類(lèi)的方法?。?!
這種說(shuō)法的確有些不合適。
第一點(diǎn),在Rectangle的setHeight方法中如果有異常拋出,那么Square的setHeight方法語(yǔ)法上是可以?huà)伋鯡xception的,但是如果Rectangle的setHeight方法中沒(méi)有拋出異常呢,Square的setHeight是不能拋的,怎么辦,如果正好Rectangle的setHeight沒(méi)有返回值呢?還要修改父類(lèi)嗎?
當(dāng)然,這只是個(gè)極端的例子,我的意思還是那句話(huà),脫離了代碼,脫離了設(shè)計(jì),空談是不是繼承關(guān)系是毫無(wú)疑義的。
第二點(diǎn),正如 truezerg(趙明宇) 所說(shuō),用戶(hù)每次給他的長(zhǎng)方形設(shè)置長(zhǎng)的時(shí)候總是發(fā)生異常,或者說(shuō)告訴他“他的行為已經(jīng)隱含的修改了Square.width”,用戶(hù)豈不是很冤枉,“我只是想調(diào)一下setHeight()”。然后你只能再告訴用戶(hù)“你現(xiàn)在調(diào)用的是正方形,不是長(zhǎng)方形”,那用戶(hù)會(huì)很生氣的“我想要只鴨子,兩條腿都能動(dòng)!你卻給我個(gè)兩條腿長(zhǎng)在一起的,我想讓它邁腿它就亂叫……我只是想要個(gè)兩條腿走路的鴨子!”
也許比喻得不恰當(dāng),但是實(shí)際情況就是這樣。
另外,TO fbysss(獨(dú)孤求敗)
這么好的帖子,留著吧,大家在這個(gè)帖子里面獲得的知識(shí)比能得到的專(zhuān)家分要多得多。
還是希望大家能在這里繼續(xù)討論。
mark
我覺(jué)得還是有必要再說(shuō)說(shuō)我的觀點(diǎn):脫離了代碼,脫離了設(shè)計(jì),空談是不是繼承關(guān)系是毫無(wú)疑義的。不同的代碼,他們的關(guān)系可能是不同的。
是不是繼承關(guān)系是人定的,而不是客觀世界,因?yàn)樗緛?lái)就不是客觀的。繼承的合適不合適才是我們討論的問(wèn)題。
《java與模式》中說(shuō)到的長(zhǎng)方形不是正方形的父類(lèi),是針對(duì)書(shū)中的那幾段代碼而說(shuō)的,而不是針對(duì)我們?cè)谛W(xué)數(shù)學(xué)里面學(xué)到那個(gè)長(zhǎng)方形與正方形。書(shū)中的那幾段代碼,正方形的確不應(yīng)該繼承長(zhǎng)方形,原因前面已經(jīng)討論很多了。
不同的代碼描述客觀世界的事物時(shí)關(guān)系可能不同。
我們可以用別的方法來(lái)寫(xiě)長(zhǎng)方形
public class Rectangle {
public int getSideCount() {
return 4;
}
public int getInteriorAngleDegree() {
return 90;
}
}
這樣寫(xiě),正方形完全可以繼承長(zhǎng)方形。這是因?yàn)樵谶@個(gè)長(zhǎng)方形里面沒(méi)有涉及到width和height是否可獨(dú)立變化這個(gè)Contract。當(dāng)然這樣的長(zhǎng)(正)方形也不能存取他們的長(zhǎng)和寬,也不能計(jì)算它們的面積。如果你的設(shè)計(jì)要求就是這些,你完全可以設(shè)計(jì)成繼承關(guān)系。反之,只要涉及到存取他們的長(zhǎng)和寬,就不能繼承。
脫離了代碼,脫離了設(shè)計(jì),空談是不是繼承關(guān)系是毫無(wú)疑義的。
書(shū)中的長(zhǎng)方形不是正方形的父類(lèi),是針對(duì)書(shū)中的那幾段代碼而說(shuō)的.
總算有人說(shuō)了明白話(huà)!
好,我留著,歡迎繼續(xù)討論!有的帖子還沒(méi)時(shí)間看真切,得慢慢研究
“到底正方形是不是長(zhǎng)方形的子類(lèi)取決于你抽象的程度和抽象的語(yǔ)義。”
我贊同 asdmonster(asd) 的主要是這句話(huà)。
是不是子類(lèi)可以擴(kuò)充父類(lèi),而不可以通過(guò)限制來(lái)特殊化
那白馬黑馬呢?這么說(shuō)黑馬不是馬的子類(lèi)
to閻博士:論壇不能注冊(cè),所以在這里說(shuō)兩句。這本書(shū)剛看完,受益匪淺,不過(guò)要做到靈活使用,看來(lái)還要很長(zhǎng)一段時(shí)間。冒昧的問(wèn)一下,博士從初學(xué)java到現(xiàn)在如此透徹的程度花了多少時(shí)間?
是不是子類(lèi)可以擴(kuò)充父類(lèi),而不可以通過(guò)限制來(lái)特殊化
那白馬黑馬呢?這么說(shuō)黑馬不是馬的子類(lèi)
------------------------------
可以特殊化,但不可以突破父類(lèi)的規(guī)則。
我們都一再?gòu)?qiáng)調(diào)了,在討論什么是不是什么的子類(lèi)的時(shí)候一定要有前提條件。一定要有特定的環(huán)境。如果單純說(shuō)白馬黑馬是不是馬的子類(lèi)的話(huà)。那說(shuō)是也對(duì),說(shuō)不是也對(duì)。 因?yàn)榇蠹覜](méi)有站在同一個(gè)環(huán)境上說(shuō)話(huà)。
OO是個(gè)好東西,但是并非萬(wàn)能。
c++雖然不是最早的OO語(yǔ)言,但是卻是早期OO語(yǔ)言中影響最大的。c++發(fā)展OO將近20年,沒(méi)有任何顯著成果。直到90年代末加入了泛型設(shè)計(jì),用STL(標(biāo)準(zhǔn)模版庫(kù))重新構(gòu)建了c++庫(kù)后,c++才真正成為了一個(gè)出色的語(yǔ)言。
java的OO能力比c++出色,這是好事。從JDK1.0開(kāi)始至今,java基于OO思想架構(gòu)的類(lèi)庫(kù)日趨完善。但是過(guò)分的OO也很容易將設(shè)計(jì)引入死胡同。產(chǎn)生“白馬非馬”、“正方形非長(zhǎng)方形”的java類(lèi),就是這種陷入誤區(qū)的典型例子。
如果你發(fā)現(xiàn)自己的設(shè)計(jì)局限性很大,無(wú)法適應(yīng)特例,那么你重新考慮架構(gòu)設(shè)計(jì)的時(shí)候來(lái)了。
呵呵,別生氣。
Square已經(jīng)更改了Rectangle的語(yǔ)義
——
我相信大家都記得這么一個(gè)東東:Deprecated.
上面有人說(shuō)過(guò)design by contract .這就是一個(gè)破壞契約的例子。因?yàn)镽ectangle缺省的語(yǔ)義是setWidth()不改變height,但是它改變了。設(shè)置Square比較合適的方式是用從Diamond繼承下來(lái)的setSide()。
剛開(kāi)始我寫(xiě)到對(duì)Square.setWidth()的時(shí)候面臨這兩種選擇,要么是使用Deprecated,要么就是拋出異常。后者可能?chē)?yán)厲了點(diǎn),但是我實(shí)在想不出別的法子讓Square屏蔽掉Rectangle的setWidth()和setHeight()方法。
這就產(chǎn)生了一個(gè)問(wèn)題:Square從Rectangle繼承了什么?我個(gè)人的理解是它繼承的是有兩條垂直的邊,僅此而已。
樓上的,既然那么為難干嗎還要繼承呀
java推薦使用集合而不是繼承。
看樣子,大家老是模式,模式的,
大家忘了有一個(gè)東西叫:原則。模式是因原則而生的。
OCP原則:老爸能去的地方,兒子一定能去。
可正方形,長(zhǎng)方形中:
public void resize(Rectangle r){
while(r.getHeight()<r.getWidth()){
r.setWidth(r.getWidth()+1);
}
}
長(zhǎng)方形能作的事,正方形不能作,違反了OCP原則。
為什么大家一定要強(qiáng)調(diào)長(zhǎng)方形的長(zhǎng)大于寬呢?
如果一定要強(qiáng)調(diào)這一點(diǎn),那么正方形的長(zhǎng)等于寬就不符合了,正方形不是長(zhǎng)方形(即正方形不是長(zhǎng)方形的子類(lèi))
但是,初中的幾何課本上就不再?gòu)?qiáng)調(diào)這一點(diǎn)了,長(zhǎng)方形(矩形)是特殊的平行四邊形,有一個(gè)腳為直角,并沒(méi)有規(guī)定那條邊長(zhǎng),那條邊短,面積等與相鄰兩條邊的乘積
同時(shí)也規(guī)定了相鄰兩邊長(zhǎng)度相等的矩形是正方形
按照這個(gè)定義:正方形就是長(zhǎng)方形的子類(lèi)
單純爭(zhēng)論長(zhǎng)方形與正方形的關(guān)系沒(méi)有意義。 不如誰(shuí)舉一個(gè)現(xiàn)實(shí)中的情況大家來(lái)討論還差不多。 要不樓主考慮結(jié)貼吧。
如《Java與模式》書(shū)中提到的java.util.Properties和Hashtable就是一個(gè)好的例子。
binny說(shuō)的很好。
父子關(guān)系是根據(jù)所支持的操作的。僅僅根據(jù)名字來(lái)說(shuō)誰(shuí)是誰(shuí)的父親沒(méi)有意義。
數(shù)學(xué)中為什么Square是Rectangle呢?因?yàn)閿?shù)學(xué)中不存在setWidth, setHeight這些過(guò)程性的變化。數(shù)學(xué)中的函數(shù)和c++/java中的函數(shù)是不同的概念。數(shù)學(xué)函數(shù)沒(méi)有副作用,而java/c++中的函數(shù)其實(shí)應(yīng)該叫做過(guò)程/方法更合適。
數(shù)學(xué)中,Square(5)是一個(gè)邊長(zhǎng)為5的正方形。它也是一個(gè)長(zhǎng)和寬都是5的矩形。
而當(dāng)我說(shuō)“把一個(gè)邊長(zhǎng)為5的正方形的一邊設(shè)為3"時(shí),那其實(shí)是意味著一個(gè)新的矩形,Rect(3,5)。
如果我們變換Square和Rect的支持的方法,父子關(guān)系就可能隨之變化。
舉個(gè)例子:
一個(gè)正方形可以當(dāng)作一個(gè)只讀的矩形來(lái)用。
一個(gè)矩形可以當(dāng)作一個(gè)只寫(xiě)的正方形來(lái)用。
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可以是一個(gè)SquareW, Square可以是一個(gè)RectR。
而Square和Rect之間卻沒(méi)有直接的聯(lián)系。
另一個(gè)例子,如果我們把setHeight(),setWidth()做成functional的,也就是模擬數(shù)學(xué)的方式生成一個(gè)新的矩形,(個(gè)人比較喜歡這種immutable的設(shè)計(jì))那么,Square就可以理直氣壯地當(dāng)成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();
}
總結(jié):
為什么正方形不是矩形?
因?yàn)槟阍O(shè)計(jì)的這個(gè)“正方形”,“矩形”不等同于數(shù)學(xué)意義上的“正方形”,“矩形”。OO設(shè)計(jì)中的父子關(guān)系要根據(jù)支持的操作。
看了很久了, 越看越明白
看了看,想了想,違反現(xiàn)實(shí)的設(shè)計(jì)就是錯(cuò)誤的。不管辯解者有如何的公孫龍。
黑馬它就是馬的子類(lèi),瘸了條腿的黑馬也是黑馬的子類(lèi)。父類(lèi)能去的地方,子類(lèi)就能去。這種替換本身就有問(wèn)題。像這種和現(xiàn)實(shí)不符的問(wèn)題說(shuō)明了oo的局限。
我贊同asdmaster的說(shuō)法,我覺(jué)得是那是一個(gè)有意義地修正。向上轉(zhuǎn)型未必完全安全。
要保證向上轉(zhuǎn)型安全,那么就要在程序的抽象世界中強(qiáng)迫保證子類(lèi)不違反父類(lèi)的行為(專(zhuān)制呀)。也就出現(xiàn)了白馬非馬的問(wèn)題。
真的希望有那位大家可以重新提出一個(gè)思想來(lái)讓程序更加貼近現(xiàn)實(shí),也更方便?,F(xiàn)在面向?qū)ο笏枷脒_(dá)到它的初衷(在計(jì)算機(jī)世界中再現(xiàn)現(xiàn)實(shí))了嗎?我覺(jué)得沒(méi)有。
面向?qū)ο蟛皇且谟?jì)算機(jī)世界中再現(xiàn)現(xiàn)實(shí)
yes! 想用OO來(lái)在計(jì)算機(jī)中模擬現(xiàn)實(shí)中的一切是非??尚Φ?br />
花了40分鐘,看完這個(gè)帖子,受益非淺。
長(zhǎng)方形和正方形,這兩個(gè),怎么說(shuō)都有道理。
世界事相對(duì)的世界、不管事什么方面,都有一個(gè)局限性,這就是oo的局限性。
如果你再實(shí)際的開(kāi)發(fā)中,非要分出這兩個(gè)關(guān)系,恐怕你會(huì)走入深淵!
你說(shuō)長(zhǎng)方形不是正方形的父類(lèi),而我就可以。
馬和黑馬的關(guān)系,在通常的意義上并不是長(zhǎng)方形和正方形的關(guān)系。
為什么??
因?yàn)閷?duì)于馬來(lái)說(shuō),應(yīng)該有一個(gè)屬性,叫做顏色。而當(dāng)這個(gè)顏色為黑的時(shí)候,那么馬就是黑馬。
而對(duì)于長(zhǎng)方形和正方形來(lái)說(shuō),很少有人會(huì)給他一個(gè)屬性,標(biāo)志他的這個(gè)屬性吧?
所以我說(shuō),我可以把正方形當(dāng)作長(zhǎng)方形的子類(lèi)。我可以給rectangle一個(gè)屬性,讓他去標(biāo)示到底是長(zhǎng)方形,還是正方形。
很遺憾,我們擁有的就只是現(xiàn)實(shí)。并沒(méi)有一個(gè)供你幻想的黑客世界。我覺(jué)得oo里面就是沒(méi)有顯式地表達(dá)條件,這個(gè)現(xiàn)實(shí)中很重要的東西,才有這樣的尷尬。
其它類(lèi)型的語(yǔ)言也是同樣的思路。像大家熟知地人工智能語(yǔ)言。很可惜,只說(shuō)明了一個(gè)問(wèn)題。人類(lèi)對(duì)現(xiàn)實(shí)的了解遠(yuǎn)不如對(duì)機(jī)器的了解。
我有一個(gè)思路是去定義一個(gè)final驗(yàn)證方法來(lái)模擬類(lèi)條件,并加到構(gòu)造方法中去。凡是符合這個(gè)條件的就是子類(lèi)。這樣不能解決大部分問(wèn)題,但是應(yīng)該比一點(diǎn)條件限制也沒(méi)有好,至少在程序的抽象世界里會(huì)比較有效。
象正方形是否是長(zhǎng)方形地問(wèn)題。只需符合這個(gè)長(zhǎng)方形地條件就可以了。有的方法失效是在現(xiàn)實(shí)中是正?,F(xiàn)象。多態(tài)的問(wèn)題,只好先驗(yàn)證一下,子類(lèi)也做點(diǎn)提示告訴用戶(hù)。
其實(shí),說(shuō)實(shí)話(huà),這個(gè)問(wèn)題挺無(wú)聊的
首先是我的題外話(huà)。OO(面向?qū)ο?,我認(rèn)為從一個(gè)C++開(kāi)發(fā)者的角度嚴(yán)格地說(shuō),我們?cè)谟懻摶趯?duì)象OB,當(dāng)然java里面已經(jīng)也有OB沒(méi)錯(cuò)但那不是地道的java,另一個(gè)話(huà)題了)是一種方法。它是一個(gè)動(dòng)詞,而不是一個(gè)名詞。it's a verb, not a noun.動(dòng)詞無(wú)所謂對(duì)錯(cuò)。“吃飯”,這個(gè)動(dòng)賓結(jié)構(gòu)的動(dòng)詞短語(yǔ)有對(duì)錯(cuò)嗎?“騎車(chē)”,有對(duì)錯(cuò)嗎?沒(méi)有。所以說(shuō)“OO里面的尷尬”我覺(jué)得不太成立,“吃飯”的尷尬?吃飯能否產(chǎn)生尷尬,能。“西方人用筷子吃飯”確實(shí)會(huì)尷尬,但那不是吃飯的尷尬。一句話(huà),看是誰(shuí)在吃,看是怎么吃而已。
其次是題外話(huà)之二。由吃飯的例子引發(fā)開(kāi)去的。吃飯是人類(lèi)取得能量的一種方法,其在人類(lèi)獲得能量的諸多方法中的地位,和今天的面向?qū)ο笤谲浖_(kāi)發(fā)中的地位頗為類(lèi)似。東方人喜歡用筷子吃飯,西方人喜歡用刀叉,這是文化的差異。但是有一點(diǎn)是一樣的,它們都是吃飯。
無(wú)論在東方人的意識(shí)中,還是在西方人的意識(shí)中,吃飯,都是表示一種涵義,就是那個(gè)含義,呵呵,你知道的,不用說(shuō),也沒(méi)法說(shuō)清楚,保證不會(huì)理解錯(cuò)。
其三,回到我們的正題。但是離不開(kāi)上面兩點(diǎn)。一般來(lái)說(shuō),吃飯要喝湯吧,就像OO要繼承一樣。問(wèn)題出在這里,重點(diǎn)來(lái)了。喝湯的概念,東西方人都一樣,可是繼承的概念,在東西方人的腦海中,以我對(duì)東西方人的觀察來(lái)說(shuō),還是有那么一點(diǎn)略微的區(qū)別。inherit,繼承。我們先看看《簡(jiǎn)明英漢詞典》如何解釋inherit:
inherit
[ in'herit ]
vt.繼承, 遺傳而得
顯然,如果詞典沒(méi)有出什么大的差錯(cuò)的話(huà),在西方人的思維里,繼承是“遺傳而得”。什么叫遺傳而得,從生物學(xué)的單親繁殖的角度來(lái)講(java就是單親繁殖),除非產(chǎn)生變異,否則子個(gè)體至少在DNA上,和母體相當(dāng)。java里面用了extends這個(gè)詞,愚以為一語(yǔ)中的,extend在英文里有“擴(kuò)充, 延伸, 伸展, 擴(kuò)大”的意思?;氐介L(zhǎng)方形和正方形的問(wèn)題上,認(rèn)為正方形是長(zhǎng)方形子類(lèi)的同學(xué)可以考察一下正方形這個(gè)類(lèi)的DNA(開(kāi)玩笑),正方形extends了長(zhǎng)方形沒(méi)有,顯然沒(méi)有。正方形is-a長(zhǎng)方形,沒(méi)錯(cuò),難道不應(yīng)該繼承嗎。注意,我們?cè)谟懻擃?lèi),沒(méi)有討論實(shí)例。java教科書(shū)中對(duì)類(lèi)的定義大致為:<b>類(lèi)是定義同一類(lèi)所有對(duì)象的變量和方法的藍(lán)圖或原型。</b>
正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例,為什么?因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)!
長(zhǎng)方形類(lèi)是擴(kuò)展的正方形類(lèi)。用集合論中的韋恩圖來(lái)表達(dá)一下會(huì)更清晰。長(zhǎng)方形是一個(gè)大類(lèi),而正方形是長(zhǎng)方形大類(lèi)中的一個(gè)小類(lèi)。用通俗的話(huà)講,長(zhǎng)方形類(lèi)比正方形類(lèi)來(lái)的大。
它繼承了正方形類(lèi)的DNA。
現(xiàn)在看看class Square : extends Rectangle 是多么愚蠢!
明明應(yīng)該是 class Rectangle : extends Square
第四,現(xiàn)在你看OO是多么悲哀,"面向?qū)ο蟛皇且谟?jì)算機(jī)世界中再現(xiàn)現(xiàn)實(shí)"。因?yàn)橐苍S你會(huì)發(fā)現(xiàn)以下?tīng)顩r:
class Rectangle : extends Square 的寫(xiě)法,估計(jì)會(huì)得罪了工程人員,這樣符合“道義”的寫(xiě)法,在工程上完全沒(méi)有意義(沒(méi)有哪個(gè)工程不是超類(lèi)先行的)。就像用某個(gè)有限狀態(tài)自動(dòng)機(jī)的狀態(tài)轉(zhuǎn)換圖來(lái)構(gòu)造一個(gè)詞法編譯器一樣對(duì)人類(lèi)來(lái)說(shuō)是不現(xiàn)實(shí)的(或許《黑客帝國(guó)III》中那個(gè)白頭發(fā)白胡子的設(shè)計(jì)師老爺爺可以完成他,理由很簡(jiǎn)單,他是機(jī)器(程序),而我們是人。)
class Square : extends Rectangle 的寫(xiě)法,估計(jì)會(huì)得罪了學(xué)院派。這明明是道義的背叛。而且會(huì)產(chǎn)生這個(gè)帖子所討論的種種問(wèn)題。原因很簡(jiǎn)單,你要繼承一個(gè)父類(lèi),你得保證你的子類(lèi)能做父類(lèi)能做的事情。在C++中你實(shí)現(xiàn)一個(gè)讀寫(xiě)漢字的fstream文件IO流,一次讀寫(xiě)兩個(gè)字節(jié)的實(shí)現(xiàn)是錯(cuò)誤的。因?yàn)槟氵@個(gè)流至少也要能搞定英文,否則,就不應(yīng)該用繼承,而應(yīng)該用復(fù)合。
第五點(diǎn),談?wù)勅缃裨谥袊?guó)的計(jì)算機(jī)著作翻譯界,都存在很多缺乏足夠功力和底蘊(yùn)的譯者。很多譯著中都沒(méi)有區(qū)分“類(lèi)”和“類(lèi)的實(shí)例”,把“繼承”和“擴(kuò)展”搞得如此云里霧里的現(xiàn)象,譯者的責(zé)任也很大。諸如此類(lèi)的現(xiàn)象還有很多。
歡迎和我交流 b00251420@sei.ecnu.edu.cn
樓上說(shuō):很多譯著中都沒(méi)有區(qū)分“類(lèi)”和“類(lèi)的實(shí)例”
真的呀?難以置信...
class vs. instance of class不加區(qū)分?還是
class vs. object不加區(qū)分?
恐怕是英文原版書(shū)就沒(méi)有區(qū)別,照我看,這不過(guò)就是軟件業(yè)專(zhuān)業(yè)術(shù)語(yǔ)不夠規(guī)范的一個(gè)例子。
舉個(gè)例子,JavaScript里面根本就沒(méi)有類(lèi)的概念,可以很多書(shū)中還要正經(jīng)八百地談?wù)?#8220;類(lèi)”,這就是屬于不規(guī)范,這種現(xiàn)象是普遍存在的,不一定是翻譯的責(zé)任。
尊敬的jeffyan77,你好。
我知道原版書(shū)就是沒(méi)有區(qū)分的。因?yàn)槲覀冇玫木褪窃鏁?shū)。你說(shuō)的是對(duì)的,這不過(guò)就是軟件業(yè)專(zhuān)業(yè)術(shù)語(yǔ)不夠規(guī)范的一個(gè)例子。看來(lái)上至博士,下至我們這種半文盲,都對(duì)此有所感觸。這是我本來(lái)的意思。但是我不想刺激國(guó)外的作者,因?yàn)樗麄兪俏业呐枷?,我知道他們是?xiě)得不夠規(guī)范,但為什么譯者們不為自己的偶像做一點(diǎn)有益的修改和補(bǔ)充。其實(shí)他們分得清" class " 和 " instance of class"。
JavaScript我不懂,也沒(méi)有看過(guò)這方面的書(shū)。我對(duì)博士的淵博表示欽佩。
最后,非常喜歡你的研究。
另外向CSDN表示歉意,最近C++和Java兩頭看,所以寫(xiě)出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;
};
最后說(shuō)明一點(diǎn),學(xué)好C++。
jvm 連 兩個(gè)分號(hào) ;; 都編譯不過(guò)。不知道代碼優(yōu)化部分是怎么寫(xiě)的。
TO: yangye1211(楊楊)
有深度
另外,我也覺(jué)得目前Java的術(shù)語(yǔ)五花八門(mén),通常一個(gè)單詞有很多中文叫法,不同的書(shū)不同的寫(xiě)法,
真的該統(tǒng)一一下了
--------------------------------------
正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例,為什么?因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)!
--------------------------------------
先不問(wèn)到底該不該繼承,上面這句話(huà)怎么說(shuō)都是錯(cuò)的。
如果:因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)
所以:正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例
那這句話(huà)正好反了。
------------------------------------------
長(zhǎng)方形類(lèi)是擴(kuò)展的正方形類(lèi)。用集合論中的韋恩圖來(lái)表達(dá)一下會(huì)更清晰。長(zhǎng)方形是一個(gè)大類(lèi),而正方形是長(zhǎng)方形大類(lèi)中的一個(gè)小類(lèi)。用通俗的話(huà)講,長(zhǎng)方形類(lèi)比正方形類(lèi)來(lái)的大。
------------------------------------------
斗膽問(wèn)一句:生物學(xué)上把所有生物分成 門(mén)、綱、目、科、屬、種 幾大范圍。
按yangye1211(楊楊) 的話(huà)講,長(zhǎng)方形的范圍比正方形大,所以長(zhǎng)方形擴(kuò)展正方形,所以長(zhǎng)方形是正方形子類(lèi)。 那整個(gè)生物界和人類(lèi)誰(shuí)的范圍大? 人類(lèi)是生物界的父類(lèi)?
例子舉的有點(diǎn)過(guò),不過(guò)在談繼承這個(gè)詞的時(shí)候,千萬(wàn)別比較誰(shuí)的范圍大。 因?yàn)檎孟喾?,范圍大的在OO里面正好是父類(lèi),雖然在java里,繼承的關(guān)鍵字用的是 extends ,有擴(kuò)展的意思。但那可不是范圍上的擴(kuò)展,而是功能上的擴(kuò)展的意思。
如果非要和現(xiàn)實(shí)扯上點(diǎn)關(guān)系的話(huà),那這個(gè)extends正好就是變異的意思。生物學(xué)上變異分為優(yōu)變異和劣變異,劣變異就是我們通常說(shuō)的退化。 extends在這里指的就是優(yōu)變異。我們?nèi)祟?lèi)比其它靈長(zhǎng)類(lèi)動(dòng)物都聰明,會(huì)制造工具等等。這些都是功能上的extends,而不是范圍上的
哈哈,哈哈哈,笑死我了。(yangye1211(楊楊)) 學(xué)英語(yǔ)的初哥跑到這里談?wù)撔┦裁囱剑?br />
無(wú)知者無(wú)畏。
哎,程序語(yǔ)言不等同于自然語(yǔ)言。不要浪費(fèi)閻博士的時(shí)間,和你辯駁這些。我這淺薄者來(lái)和你說(shuō)句話(huà):“回去編兩天程序,再發(fā)言。”
我喜歡聽(tīng)到不同意見(jiàn),喜歡聽(tīng)到批評(píng)。但是說(shuō)句自私的話(huà),一般來(lái)說(shuō)誰(shuí)不是喜歡和比自己強(qiáng)的人討論。
因此就回復(fù)一點(diǎn),就是truezerg提出的第一點(diǎn)。
“
先不問(wèn)到底該不該繼承,上面這句話(huà)怎么說(shuō)都是錯(cuò)的。
如果:因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)
所以:正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例
那這句話(huà)正好反了。
”
這兩句話(huà)沒(méi)毛病吧?
搞清楚類(lèi)和實(shí)例。學(xué)習(xí)一下集合論。
"因?yàn)檎孟喾?,范圍大的在OO里面正好是父類(lèi)"
關(guān)于這句,重申我的觀點(diǎn),吃飯本沒(méi)有尷尬,看你怎么吃。任何話(huà)說(shuō)得太絕對(duì),它一定可能有點(diǎn)小毛病。
我學(xué)東西很慢,不能和樓上寫(xiě)過(guò)大程序的陽(yáng)光燦爛兄比。見(jiàn)諒。
我只是覺(jué)得學(xué)編程,我要有專(zhuān)業(yè)精神。因?yàn)槲沂擒浖こ踢@個(gè)專(zhuān)業(yè),不是隨便寫(xiě)寫(xiě)騙騙外行人,所以時(shí)間上我也賠不起,陽(yáng)光燦爛兄見(jiàn)諒。
吃飯的時(shí)候想起來(lái)的問(wèn)題。
提出來(lái)給大家批評(píng):
從長(zhǎng)方形到正方形,不存在數(shù)據(jù)抽象上的差異。都是兩條邊長(zhǎng),int 長(zhǎng)和int 寬。
存在的只是方法上的差異。設(shè)置長(zhǎng)和寬的方法有差異,所以這里最好不要用派生繼承,
而用復(fù)合繼承。正方形是長(zhǎng)方形的子類(lèi)(在數(shù)學(xué)定義上),正方形等于長(zhǎng)方形(在數(shù)據(jù)抽象上)
如果在數(shù)據(jù)抽象上存在差異,比如
class Vehicle
|
class WheeledVehicle class Boat
|
class Car class Bicycle
|
class Two-door class Four-door
那用派生繼承比較好。
如果在數(shù)據(jù)抽象上存在差異,在方法實(shí)現(xiàn)上也存在差異。這樣的例子也有吧。(虛函數(shù))
to: yangye1211(楊楊)
其實(shí)關(guān)于長(zhǎng)方形和正方形的討論已經(jīng)沒(méi)有什么必要了。 我是針對(duì)你的那二句話(huà)來(lái)說(shuō)的。 就是
-----------------------------------------
正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例,為什么?因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)!
長(zhǎng)方形類(lèi)是擴(kuò)展的正方形類(lèi)。用集合論中的韋恩圖來(lái)表達(dá)一下會(huì)更清晰。長(zhǎng)方形是一個(gè)大類(lèi),而正方形是長(zhǎng)方形大類(lèi)中的一個(gè)小類(lèi)。用通俗的話(huà)講,長(zhǎng)方形類(lèi)比正方形類(lèi)來(lái)的大。
-----------------------------------------
你還是認(rèn)為
如果:因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)
所以:正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例
這樣的結(jié)論是對(duì)的嗎?
寫(xiě)成代碼是不是更清楚一點(diǎn),比如:
class A extends B {}
你說(shuō) 誰(shuí) is-a 誰(shuí)? 是 A is-a B 對(duì),還是 B is-a A 對(duì)。 子類(lèi)的實(shí)例是一種父類(lèi)的實(shí)例,所以應(yīng)該是 A is-a B 嘛。 用長(zhǎng)方形和正方形分別替換掉 A和B, 不就是
長(zhǎng)方形類(lèi) extends 正方形類(lèi)
那不就是
長(zhǎng)方形類(lèi)的實(shí)例 is-a 正方形類(lèi)的實(shí)例嘛。 按你說(shuō)的,不是正好反了?? 所以我說(shuō)不管長(zhǎng)方形和正方形(其實(shí)我們完全可以?huà)侀_(kāi)長(zhǎng)方形和正方表了)應(yīng)不應(yīng)該有這種繼承關(guān)系。你的那句話(huà)都是錯(cuò)的。
我不明白你所說(shuō)的集合論和類(lèi)的繼承有什么關(guān)系。 麻煩還請(qǐng)楊楊兄指教一下。
另外我說(shuō):
“因?yàn)檎孟喾矗秶蟮脑贠O里面正好是父類(lèi)”這句話(huà)是針對(duì)你的論點(diǎn)而言的。 當(dāng)然不是絕對(duì)的。 但按你的用“范圍”這樣的觀點(diǎn)來(lái)推的話(huà),在OO里面確實(shí)是范圍大的是父類(lèi)。 因?yàn)楦割?lèi)是一般的,子類(lèi)是特殊的,換句話(huà)說(shuō),子類(lèi)是有“個(gè)性”的。 個(gè)性的東西怎么能大于一般的? 而且你用“范圍”這個(gè)角度是判斷類(lèi)的關(guān)系,這本身就是錯(cuò)誤的。 原因我上面都說(shuō)了。 如果楊楊兄還有自己的關(guān)點(diǎn),希望能抽出點(diǎn)時(shí)間來(lái)這里說(shuō)明一翻,大家好可以互相討論學(xué)習(xí)。
呵呵,討論又開(kāi)始啦,大家加油
繼承嗎,就是為了我們coding起來(lái)容易,閱讀起來(lái)清晰。
我認(rèn)為并不一定要和集合聯(lián)系起來(lái),
當(dāng)然一談?wù)摰嚼^承的關(guān)系,很容易讓人聯(lián)想到誰(shuí)屬于誰(shuí),誰(shuí)包含誰(shuí)
這樣想起來(lái)是容易一些
但繼承不是純粹的集合關(guān)系,
A是B的子集,不一定A就是B的子類(lèi),也不能說(shuō)B就是A的子類(lèi)。
舉個(gè)例子
太陽(yáng)系是銀河系的子集,但是如果讓你用程序描述的話(huà),它們之間能相互繼承什么東西呢
但是非要和集合拉上關(guān)系,那么外界能看到的父類(lèi)方法的確應(yīng)該是能看到的子類(lèi)方法的子集
我沒(méi)讀過(guò)專(zhuān)門(mén)的講述OO的書(shū),所說(shuō)的一切都是我自己的想法,肯定會(huì)有錯(cuò)誤的,希望大家指正。
并且希望能幫我推薦一本比較好的書(shū),不要太厚哦,博士那本書(shū)我啃了還不到四分之一呢。
路過(guò)的,隨便說(shuō)兩句。
同意asdmonster(asd) 的說(shuō)法
個(gè)人感覺(jué)這里主要的問(wèn)題是子類(lèi)--父類(lèi)關(guān)系 和 對(duì)象--類(lèi)關(guān)系的區(qū)分問(wèn)題。
說(shuō)一點(diǎn)自己的看法。
類(lèi)= 定義式
對(duì)象= 根據(jù)類(lèi)的定義產(chǎn)生的具體實(shí)例,它不能違反類(lèi)的定義,因此“一個(gè)正方形”不“是”“矩形類(lèi)”的實(shí)例,但是你可以把它“當(dāng)成”矩形。
子類(lèi)= 被特化的類(lèi)定義。它“基本上”遵循父類(lèi)的定義規(guī)則,但是在某些地方使用了自己的定義規(guī)則。因此某些父類(lèi)可以做的事子類(lèi)可能不能做。
例如
類(lèi)People擁有WalkTo(place)的能力。
類(lèi)Man繼承了People,但是可能在WalkTo(place)中增加了一些限制:
使得你調(diào)用a man.WalkTo(女浴室)時(shí)會(huì)報(bào)錯(cuò)。
現(xiàn)在有一個(gè)叫Jane的人,
當(dāng)調(diào)用Jane.WalkTo(女浴室)時(shí)發(fā)生如下錯(cuò)誤“一個(gè)男士是不應(yīng)該去這種地方的”是不會(huì)讓人覺(jué)得可疑的。
我覺(jué)得計(jì)算機(jī)是一種工具,因?yàn)橛辛诵畔⒒臅r(shí)代才會(huì)出現(xiàn)計(jì)算機(jī),為了操作者的方便出現(xiàn)了操作系統(tǒng),然后呢就有了現(xiàn)在的應(yīng)用軟件,開(kāi)發(fā)語(yǔ)言。 他們的存在是因?yàn)樗麄兎蠒r(shí)代的要求,哲學(xué)上講事物的正確性是有一定物質(zhì)條件的,事物的對(duì)與錯(cuò)在物質(zhì)條件發(fā)生改變的時(shí)候就會(huì)發(fā)生改變。為什么正方形要繼承長(zhǎng)方形因?yàn)?,因?yàn)樵贠O理念這的確是對(duì)的,那么在數(shù)學(xué)理念里這有是錯(cuò)的,這就產(chǎn)生了兩種不同的物質(zhì)條件。
OO只不過(guò)是一種設(shè)計(jì)思想,他的存在是為了我們更好的設(shè)計(jì),所以我們沒(méi)有必要去討論對(duì)與錯(cuò),只能說(shuō)怎么樣才能做出更好的設(shè)計(jì),而更好的設(shè)計(jì)是根據(jù)你的需要而改變的,有可能你的系統(tǒng)中需要resize()方法,而我的系統(tǒng)中并不需要resize()方法,所有這樣產(chǎn)生的結(jié)論也會(huì)不同,我覺(jué)得非要定義誰(shuí)去繼承誰(shuí)并沒(méi)有實(shí)際的意義,需要根據(jù)具體的情況而定
“面向?qū)ο蠓椒ǖ谋举|(zhì),就是主張從客觀世界固有的事物出發(fā)來(lái)構(gòu)造系統(tǒng),提倡用人類(lèi)在現(xiàn)實(shí)生活中常用的思維方法來(lái)認(rèn)識(shí)、理解和描述客觀事物,強(qiáng)調(diào)最終建立的系統(tǒng)能夠映射問(wèn)題域,即:系統(tǒng)中的對(duì)象以及對(duì)象之間的關(guān)系能夠如實(shí)地反映問(wèn)題域中固有事物及其關(guān)系。”
上面這段話(huà)你可以在任何關(guān)于面向?qū)ο蟮睦碚摰慕滩纳献x到。
這是oo的總綱。
呵呵。工具,這么小瞧工具呀!我們就是伺候工具地。計(jì)算機(jī)是骨骼,我們是肌肉。連為一體。這就叫做人機(jī)合一。不是你想不想合的問(wèn)題。
具體情況就是現(xiàn)實(shí)中地問(wèn)題域,設(shè)計(jì)中不符合就改掉。削足適履?否,這叫適應(yīng)環(huán)境。
使用"is-a"和"has a"測(cè)試。
長(zhǎng)方形 has a 正方形
正方形 is a 長(zhǎng)方形。
通過(guò),所以這樣的繼承關(guān)系是正確的。
呵呵,大家自由討論,不要有壓力。
在UML出現(xiàn)之前,確實(shí)有很多圖,其中有一些現(xiàn)在也可以用。譬如Venn Diagram,就是集合論中的那個(gè),就可以用來(lái)描述一個(gè)類(lèi)的responsibility。
參見(jiàn)
Designing Object-Oriented Software
Rebecca Wirfs-Brock...
1990 Prentice Hall
我們班上有兩個(gè)同學(xué)我比較喜歡與其討論學(xué)術(shù)話(huà)題。N節(jié)日語(yǔ)課都被浪費(fèi)在無(wú)休止的無(wú)意義的(至少在旁人看來(lái))討論上了。真對(duì)不去漂亮的日語(yǔ)老師MM啊。
小曾是個(gè)山東漢,運(yùn)動(dòng)會(huì)上鉛球隨便一扔就為我們班拿了個(gè)第一,但是他今年沒(méi)去年扔得遠(yuǎn),沒(méi)有破掉他去年創(chuàng)下的院紀(jì)錄。其實(shí)我每年都讓他悠著點(diǎn),這樣年年都有記錄破,我們班的分也會(huì)更高,可我懷疑他在鉛球場(chǎng)上一使勁就忘了。這家伙在日語(yǔ)課上看j2ee那本紅書(shū)的時(shí)候回過(guò)頭對(duì)我說(shuō)他立志于設(shè)計(jì)和模式,我藐視地對(duì)他說(shuō),設(shè)計(jì)已經(jīng)睡了。真的已經(jīng)睡了。(Martin Fowler說(shuō)設(shè)計(jì)已經(jīng)死了,但我認(rèn)為設(shè)計(jì)沒(méi)死,但是睡了。)他瞪大了眼睛對(duì)我說(shuō),胡說(shuō),就說(shuō)上次那個(gè)操作系統(tǒng)的文件系統(tǒng)作業(yè)項(xiàng)目,全班就沒(méi)人設(shè)計(jì)的比我好。我說(shuō),拜托,不要跟班上的人比(潛臺(tái)詞是其實(shí)班上懂設(shè)計(jì)的有幾個(gè),將來(lái)都是做TechnicalSupport和Marketing的料),隨便拉一個(gè)在公司干過(guò)2年以上的,做的肯定比你好,有點(diǎn)追求啊。我和他說(shuō),要實(shí)踐才能出真知?,F(xiàn)在人類(lèi)剛剛學(xué)會(huì)造房子,絕大部分人都在造方方正正的那種房子。方方正正的房子誰(shuí)不會(huì)設(shè)計(jì),只要是個(gè)工頭他就會(huì)設(shè)計(jì)。你說(shuō)你能設(shè)計(jì)悉尼歌劇院,一是沒(méi)有哪家公司有那么多高智商的建筑工有本事去建,二是還沒(méi)有這種需求。既然大家都是四四方方的房子,那大家也都覺(jué)得挺滿(mǎn)意。(第一個(gè)原因是主要的,因?yàn)樯a(chǎn)力決定生產(chǎn)關(guān)系嘛,等能建這種房子的公司多了,自然會(huì)有富人想住設(shè)計(jì)超前的房子來(lái)滿(mǎn)足自己對(duì)優(yōu)越感的追求的)所以我說(shuō),設(shè)計(jì)睡了。你還是多實(shí)踐實(shí)踐啊。他的回答是,我不需要實(shí)踐,只要項(xiàng)目擺在我面前我就能做,而且我對(duì)做項(xiàng)目也不感興趣,我的目標(biāo)是分析到中層。這點(diǎn)我相信。Gates現(xiàn)在讓我設(shè)計(jì)下一代操作系統(tǒng)我也能啊,支持B2D(Business to desktop),支持超級(jí)代碼托管(在某些情況下,根本就不生成代碼),支持SSL3.x with RWPI(實(shí)世界個(gè)人身份確認(rèn)),強(qiáng)不?肯定強(qiáng),Gates也要說(shuō)我強(qiáng),但是我要100年才能完成中層設(shè)計(jì),Bill一定會(huì)將美國(guó)國(guó)罵脫口而出。
后來(lái)我就把這個(gè)帖子的事情說(shuō)給他聽(tīng),他脫口而出就是應(yīng)該把正方形和長(zhǎng)方形看成一個(gè)類(lèi),然后又舉了MFC做例子。小子好樣的,證明我沒(méi)看錯(cuò)人(雖然離我還有點(diǎn)差距,我還知道DNA哈哈哈哈)。然后他的感慨就是不能為了繼承而繼承,很多人現(xiàn)在是輕設(shè)計(jì),重coding,我也有同感,其實(shí)面向?qū)ο蟀?,?shù)據(jù)抽象才是靈魂。就像一個(gè)人來(lái)講,他的DNA決定了他是他,決定了他哪里多長(zhǎng)一塊肉,哪里少長(zhǎng)一塊肉。他是誰(shuí),他屬于哪一類(lèi),并不是由他的行為,他做了什么事情來(lái)決定的。差距?。ㄖ劣谡l(shuí)和誰(shuí)的差距,見(jiàn)上面那個(gè)“吃飯”的典故)。然后又討論了一點(diǎn)其他話(huà)題。
今天和惠豬的討論比較簡(jiǎn)短,這家伙和我們倆個(gè)完全相反,整天埋頭做PHP項(xiàng)目。票子賺了不少,可是我覺(jué)得他應(yīng)該多多參加我們的討論,畢竟他對(duì)j2ee的理解還停留在jsp階段。但是我覺(jué)得他比我們倆個(gè)在有些方面都強(qiáng),至少有實(shí)踐機(jī)會(huì),我很多東西都要問(wèn)他。我就一直想找個(gè)實(shí)踐機(jī)會(huì)以后面試問(wèn)起來(lái)也光彩點(diǎn),怎么說(shuō)也算個(gè)工作經(jīng)驗(yàn)啊?,F(xiàn)在有些人用java做了點(diǎn)東西就牛得不行,其實(shí)那些東西用VB做做也可以,瞧不起VB程序員就因?yàn)槿思冶饶阍绯鰜?lái)混兩年,你看,這什么邏輯。
這個(gè)問(wèn)題搞的有點(diǎn)個(gè)人味道很重了。
年輕人有火氣挺正常,但也看清楚世界。什么vb早混兩年的話(huà)也出來(lái)了。你問(wèn)問(wèn)有多少java程序員是直接上來(lái)搞它的。還不是都從vb,c++這些上轉(zhuǎn)過(guò)來(lái)的。我就搞了4年的vb。java也弄到第三個(gè)年頭了。別人更不用說(shuō)了。到這里發(fā)言的那個(gè)不是身經(jīng)百戰(zhàn)。
討論問(wèn)題,就討論問(wèn)題。其實(shí)這個(gè)設(shè)計(jì)也可以用復(fù)合。但是我的意見(jiàn)還是:繼承關(guān)系是成立的。設(shè)計(jì)是藝術(shù),是可以靈活的,有多種選擇的。
中國(guó)軟件業(yè)最大的悲哀就是:程序員太多了。
這是站在技術(shù)和雇員的角度說(shuō)的。
to 楊楊:
..................
正方形類(lèi)的實(shí)例,確實(shí)is-a長(zhǎng)方形類(lèi)的實(shí)例,為什么?因?yàn)殚L(zhǎng)方形類(lèi)extends正方形類(lèi)!
長(zhǎng)方形類(lèi)是擴(kuò)展的正方形類(lèi)。用集合論中的韋恩圖來(lái)表達(dá)一下會(huì)更清晰。長(zhǎng)方形是一個(gè)大類(lèi),而正方形是長(zhǎng)方形大類(lèi)中的一個(gè)小類(lèi)。用通俗的話(huà)講,長(zhǎng)方形類(lèi)比正方形類(lèi)來(lái)的大。
它繼承了正方形類(lèi)的DNA。
現(xiàn)在看看class Square : extends Rectangle 是多么愚蠢!
明明應(yīng)該是 class Rectangle : extends Square
..........................
我不同意這樣的說(shuō)法。這不顛倒嗎?正方形是長(zhǎng)和寬一致的,所有的長(zhǎng)方形都有這特性?
。。。。。。。。。。。。。。
至少在DNA上,和母體相當(dāng)。java里面用了extends這個(gè)詞,愚以為一語(yǔ)中的,extend在英文里有“擴(kuò)充, 延伸, 伸展, 擴(kuò)大”的意思?;氐介L(zhǎng)方形和正方形的問(wèn)題上,認(rèn)為正方形是長(zhǎng)方形子類(lèi)的同學(xué)可以考察一下正方形這個(gè)類(lèi)的DNA(開(kāi)玩笑),正方形extends了長(zhǎng)方形沒(méi)有,顯然沒(méi)有。正方形is-a長(zhǎng)方形,沒(méi)錯(cuò),難道不應(yīng)該繼承嗎。注意,我們?cè)谟懻擃?lèi),沒(méi)有討論實(shí)例。java教科書(shū)中對(duì)類(lèi)的定義大致為:
。。。。。。。。。。。。。。。。。。。。
就extends一詞的理解,我想你把它當(dāng)成“include”了
“延伸”,我認(rèn)為是extend最確切的涵義,何為延伸?起碼得保證本體吧,如果本體的性質(zhì)都不符合,那叫什么延伸?
。。。。
正方形extends了長(zhǎng)方形沒(méi)有
。。。。
把這里的長(zhǎng)方形說(shuō)成是矩形,應(yīng)該正規(guī)一些。
正方形就是在符合長(zhǎng)方形的所有共性的基礎(chǔ)上,多了一個(gè)長(zhǎng)和寬保持相等的特性。這才是所謂“擴(kuò)展”,我認(rèn)為這正是"extends".
剛發(fā)那貼的時(shí)候,我只能理解到這個(gè)程度。
我那貼只是說(shuō)了點(diǎn)別人還沒(méi)看清的問(wèn)題,但是回避了我看不清的問(wèn)題。
現(xiàn)在我發(fā)現(xiàn)哈哈,等同的東西,誰(shuí)大誰(shuí)???
你的意思是長(zhǎng)方形>=正方形
那我是不是可以從你上面的話(huà)里推出 長(zhǎng)方形<=正方形
還是那句話(huà),不要用人的行為來(lái)給人分類(lèi)。“它不能進(jìn)女浴室所以它是男的”,這不是因果顛倒了嗎?
博士和學(xué)士的差別就在于博士知道二叉樹(shù)和最多有兩個(gè)子樹(shù)的有序樹(shù)之間的區(qū)別。
(個(gè)人看法,我本科還沒(méi)畢業(yè))
就大小問(wèn)題,我先不作結(jié)論,就下面的話(huà):
。。。。。。。。。
那我是不是可以從你上面的話(huà)里推出 長(zhǎng)方形<=正方形
。。。。。。。。。
怎么推?還請(qǐng)發(fā)表高見(jiàn)。
to 自己:
把這里的長(zhǎng)方形說(shuō)成是矩形,應(yīng)該正規(guī)一些。
正方形就是在符合長(zhǎng)方形的所有共性的基礎(chǔ)上,多了一個(gè)長(zhǎng)和寬保持相等的特性。這才是所謂“擴(kuò)展”,我認(rèn)為這正是"extends".
所謂言多必失。剛才這段話(huà),現(xiàn)在想起來(lái)是有問(wèn)題的。其實(shí)在上面已經(jīng)討論很多次了,而這句話(huà)又走了老路。因?yàn)榫退闶怯懻摂?shù)學(xué)概念,正方形概念應(yīng)該小于長(zhǎng)方形(矩形)。也就是說(shuō),長(zhǎng)方形里面包含有正方形這種特例了。正方形再?gòu)拈L(zhǎng)方形繼承(或者說(shuō)擴(kuò)展extends),看來(lái)是不好的了。
我自己現(xiàn)在差不多已經(jīng)搞清楚了。大家還要不要繼續(xù)討論?
沒(méi)關(guān)系。列寧說(shuō)你越想駁斥真理就越發(fā)現(xiàn)它的正確性。
感覺(jué)在解讀哥巴德赫猜想似的,有意思
TO:yangye1211(楊楊)
說(shuō)實(shí)話(huà), 你對(duì)extends的理解是有些偏差的?!〗o我的感覺(jué)你總是在比較誰(shuí)大誰(shuí)小, 繼承關(guān)系不是看誰(shuí)大誰(shuí)小來(lái)決定的?!xtends是擴(kuò)展,前面我已經(jīng)說(shuō)了?!∵@個(gè)擴(kuò)展是指功能上的,行為上的,能力上的,不是你所說(shuō)的范圍上的,正象 fbysss(獨(dú)孤求敗) 所說(shuō),你把extends當(dāng)成include看來(lái)了,這是錯(cuò)誤的。另外我也不贊成繼承關(guān)系靠抽象數(shù)據(jù)來(lái)獲得。 抽象行為更是獲得繼承關(guān)系的依據(jù)。
今天大概翻了一下《JAVA編程思想》,覺(jué)得我錯(cuò)了。
從正方形到長(zhǎng)方形,使用繼承,向上轉(zhuǎn)型是安全的。
所以應(yīng)該使用繼承。
但是要使用正確(指符合邏輯)的覆蓋的方法。
子類(lèi)開(kāi)發(fā)者任重道遠(yuǎn)。 :—)(所幸需要繼承的情況比較少)
up
no,no,no,我們可不是說(shuō)你錯(cuò)在正方形不繼承長(zhǎng)方形上?!∈聦?shí)上應(yīng)該不應(yīng)該繼承要分情況而定?!∥宜赋瞿斫庥姓`的地方是指:
1.范圍大的繼承范圍小的,這樣一個(gè)思想
2.如果 A is-a B,則 B是A的類(lèi)子這個(gè)錯(cuò)誤理解(您理解反了)
關(guān)于正方形到底該不該繼承長(zhǎng)方形有它的特殊情況和應(yīng)用環(huán)境在里面,紙上談兵對(duì)這個(gè)問(wèn)題沒(méi)有統(tǒng)一用處。
其實(shí)這個(gè)問(wèn)題可以追溯到j(luò)ava里對(duì)象中的非靜態(tài)成員如何分配內(nèi)存的問(wèn)題。
在這里就不展開(kāi)了。
歡迎樓上各位補(bǔ)充。謝謝。
關(guān)于正方形到底該不該繼承長(zhǎng)方形并不是一個(gè)可以用“特殊情況”或者“應(yīng)用環(huán)境”來(lái)搪塞的問(wèn)題,這個(gè)問(wèn)題的分歧說(shuō)明了目前軟件從業(yè)人員魚(yú)龍混雜的現(xiàn)狀。
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(楊楊)
關(guān)于正方形到底該不該繼承長(zhǎng)方形并不是一個(gè)可以用“特殊情況”或者“應(yīng)用環(huán)境”來(lái)搪塞的問(wèn)題,這個(gè)問(wèn)題的分歧說(shuō)明了目前軟件從業(yè)人員魚(yú)龍混雜的現(xiàn)狀。
_____________________________________________________________________________
不要亂扣帽子,大家是在討論問(wèn)題,并不能就說(shuō)明什么現(xiàn)狀
下面討論問(wèn)題
舉個(gè)例子,你認(rèn)為長(zhǎng)整形Long和整形Integer之間是什么關(guān)系?
Long的范圍包括Integer的范圍,
也就是說(shuō),Integer是Long的子集
按照你的理論是不是要繼承一下呀?
但是SUN并不這么做
他們是并列的,都繼承自Number
我并不是說(shuō)SUN做的東西就完全正確,
但是到目前我自己還沒(méi)有看到對(duì)這種做法有異議的言論
我的理論有沒(méi)有人理解?
我的理論是長(zhǎng)方形要繼承正方形?
還是理解的人已經(jīng)不發(fā)言了。
我們這里不管sun是怎么做的,sun不對(duì)我的面向?qū)ο笳n程成績(jī)負(fù)責(zé),也不對(duì)任何學(xué)術(shù)討論負(fù)
責(zé)。因?yàn)槿思瓤梢詮男詣e的角度來(lái)劃分(有男人,女人,清華的女博士三類(lèi))也可以從國(guó)籍的
角度來(lái)劃分(比如中國(guó)人,非中國(guó)人,假洋鬼子三類(lèi)),所以把sun或是什么跑不跑題扯進(jìn)來(lái)
是沒(méi)有意思的。長(zhǎng)方形和正方形在數(shù)據(jù)抽象上是一樣的。在數(shù)據(jù)抽象上:
長(zhǎng)方形=正方形。
好,那么長(zhǎng)方形>=正方形 和 正方形<=長(zhǎng)方形都對(duì)了吧。
就說(shuō)這么多,繼續(xù)看Inside The JVM
哦,是長(zhǎng)方形<=正方形,筆誤
Mr. jiganghao(JH) :
Will the overriding be helpful in our problem?
If true, how does it take an effect?Thx.
--------------------------------------
關(guān)于正方形到底該不該繼承長(zhǎng)方形并不是一個(gè)可以用“特殊情況”或者“應(yīng)用環(huán)境”來(lái)搪塞的問(wèn)題,這個(gè)問(wèn)題的分歧說(shuō)明了目前軟件從業(yè)人員魚(yú)龍混雜的現(xiàn)狀。
--------------------------------------
暈~~~
yangye1211(楊楊)所說(shuō)的觀點(diǎn)有一些新穎的地方。
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)
你誤解我的意思了
我舉這個(gè)例子就是和yangye1211(楊楊)說(shuō),并不是有集合關(guān)系就要繼承。
所以正方形和長(zhǎng)方形之間的關(guān)系,不一定非的要用繼承關(guān)系。
用不用繼承是和具體情況有關(guān)系的。
TO: yangye1211(楊楊)
其實(shí)這個(gè)帖子剛開(kāi)始討論的時(shí)候我也考慮過(guò)讓長(zhǎng)方形繼承正方形,但是考慮之后覺(jué)得,這樣繼承只在非常特殊的條件下才成立
1. 長(zhǎng)方形的邊長(zhǎng)(長(zhǎng)和寬)符合一定的函數(shù)關(guān)系,正方形中邊長(zhǎng)使用一個(gè)變量來(lái)表示,設(shè)置邊長(zhǎng)用一個(gè)方法就可以了,但長(zhǎng)方形有兩個(gè)邊長(zhǎng)(長(zhǎng)和寬)呢,怎么辦,一個(gè)方法同時(shí)給兩個(gè)邊(長(zhǎng)和寬)賦值嗎,那么這兩個(gè)邊(長(zhǎng)和寬)就只能符合一定的函數(shù)關(guān)系了,這樣才能把本來(lái)是給一個(gè)值賦值的數(shù)賦給兩個(gè)值?我想你的想法也許是在長(zhǎng)方形里重新寫(xiě)給邊長(zhǎng)賦值的方法,那么面積呢?也重新寫(xiě)嗎?那么這樣有哪些不用重寫(xiě)呢?四條邊和四個(gè)內(nèi)角是直角。呵呵,那就去看看我2003-11-11 10:56:19的留言。
2. 只能有長(zhǎng)方形和正方形這兩個(gè)類(lèi),或者說(shuō)是有很少的類(lèi)。否則,會(huì)有長(zhǎng)方形繼承正方形,平行四邊形繼承長(zhǎng)方形,四邊形繼承平行四邊形……,那么四邊形從正方形那里能繼承來(lái)什么呢,設(shè)置邊長(zhǎng)還是求面積?這次連內(nèi)角都不一樣了?;旧纤械姆椒ǘ疾幌嗤家貙?xiě),那要繼承有什么用呢。
所以我說(shuō)正確的繼承是有條件的。能否正確的繼承要看具體環(huán)境和你要實(shí)現(xiàn)的功能。
不能亂繼承,要符合"is-a"原則。有繼承關(guān)系的但是可以不處理成這種關(guān)系。反之不可。
根據(jù)長(zhǎng)和寬可以唯一確定一個(gè)長(zhǎng)方形,根據(jù)長(zhǎng)和寬可以唯一確定一個(gè)四邊形嗎?不行的呀,四
邊形多了一個(gè)數(shù)據(jù),就是內(nèi)角角度。用四邊形繼承長(zhǎng)方形,那不是問(wèn)題大了。
至少要知道一個(gè)內(nèi)角的角度,再加上兩條邊長(zhǎng),才能唯一確定一個(gè)四邊形。因此四邊形
的數(shù)據(jù)抽象和長(zhǎng)方形不同了,如果繼承了,那么以后難免會(huì)出問(wèn)題。比如求面積。
重點(diǎn)來(lái)了:
小學(xué)數(shù)學(xué)老師一定告訴過(guò)你:“判斷一個(gè)整數(shù)是否能被3整除,只要看它各位數(shù)字相加的和
是否能被3整除。”這個(gè)法則完美解決了判斷一個(gè)整數(shù)是否能被3整除的問(wèn)題,但是這不是理
論,但這只是經(jīng)驗(yàn)法則。身為小學(xué)生的你,只是照著這個(gè)去做罷了。理論的證明還是要留待高
年級(jí)學(xué)過(guò)了一些代數(shù)知識(shí)才能解決。這個(gè)規(guī)則告訴你怎么判斷,但沒(méi)有告訴你為什么。
現(xiàn)在搞清楚理論和法規(guī)的區(qū)別?
法律規(guī)定,盜竊是有罪的。所以你不能盜竊,但是法律有一點(diǎn)點(diǎn)小缺陷——它沒(méi)有告訴你為什
么盜竊是有罪的。
LSP告訴你,違反LSP的繼承是有罪的。但是LSP沒(méi)有告訴你為什么。
所以有了這個(gè)帖子的討論。
其實(shí),LSP只是法律,并不是真理。
在和平時(shí)期殺人有罪,在戰(zhàn)爭(zhēng)時(shí)期殺人未必有罪。
抱著表面的LSP不放,必然會(huì)產(chǎn)生樓上各位的諸多煩惱。
一個(gè)Object大概可以分為兩部分,它的數(shù)據(jù)和它的方法(行為)
LSP如果更清晰地來(lái)表述,應(yīng)該是,
1)在方法可以被覆蓋的前提下,子類(lèi)的數(shù)據(jù)抽象一定要和父類(lèi)的數(shù)據(jù)一致,否則請(qǐng)用
composition。
2)在方法不可以被覆蓋的前提下,或是不提倡子類(lèi)方法覆蓋父類(lèi)方法的前提下,父類(lèi)出現(xiàn)的
地方,一定要可以用子類(lèi)來(lái)替換,否則請(qǐng)用composition。
以上是我對(duì)LSP的歸納。
我們這個(gè)問(wèn)題,是子類(lèi)可以覆蓋父類(lèi)的方法的前提下,所以,不要用第2條來(lái)判斷正方形是否
可以繼承長(zhǎng)方形。請(qǐng)用第一條。
歡迎指正。
第一次來(lái)java學(xué)習(xí),就看見(jiàn)了好貼子,忍不住也說(shuō)說(shuō)自己的看法
在說(shuō) A is-a B的時(shí)候,是必須要考慮到B和A的方法和屬性,單純的說(shuō)A is-a B是無(wú)意義的。
在設(shè)計(jì)中,我們?cè)O(shè)計(jì)長(zhǎng)方形和正方形的時(shí)候肯定是要為這兩個(gè)類(lèi)定義方法和屬性的。我覺(jué)得只有在定義了方法和屬性的基礎(chǔ)之上我們才能考慮類(lèi)之間的關(guān)系是不是繼承關(guān)系。脫離方法和屬性是沒(méi)有意義的
大家指教
大家別討論正方形和長(zhǎng)方形的關(guān)系了。 單純的討論這個(gè)沒(méi)有什么意義。
不如大家談?wù)勗谀銈兏髯缘膶?shí)際項(xiàng)目中是如何處理類(lèi)之間的關(guān)系的。可以舉實(shí)際中的例子。并說(shuō)說(shuō)自己的分析過(guò)程。 我想了解別人分析的過(guò)程比單純的爭(zhēng)論一個(gè)結(jié)果好得多。
不知道這算不算統(tǒng)一了?
我贊成qiujoe(迷糊) ,我也覺(jué)得如果前提都不一樣討論就沒(méi)有價(jià)值了。
jiganghao(JH) :
LSP確實(shí)是進(jìn)行好的設(shè)計(jì)的指導(dǎo)原則,但所有的規(guī)則都是可以破壞掉的。如果LSP強(qiáng)制執(zhí)行的話(huà),Java語(yǔ)言就沒(méi)有用了。任何一個(gè)Java類(lèi)都是java.lang.Object的子類(lèi),幾乎所有的Java類(lèi)都需要有Object所沒(méi)有的方法和屬性。
因此研究LSP的方向是在什么時(shí)候應(yīng)當(dāng)遵守,什么時(shí)候應(yīng)當(dāng)破壞。這個(gè)研究就導(dǎo)致了DbC,而Contract就可以作為一個(gè)集合描述,因此使用Venn Diagram是很自然的。
你所說(shuō)的,長(zhǎng)方形和正方形的關(guān)系depends具體情形的觀點(diǎn)我是同意的,這個(gè)分析的準(zhǔn)則也應(yīng)當(dāng)是DbC。你寫(xiě)一個(gè)Java類(lèi),這個(gè)類(lèi)和Object類(lèi)之間的Contract不限制你給出更多的方法和屬性,因此你可以這樣做。在長(zhǎng)方形和正方形的情形下,這個(gè)Contract是什么,在具體情形下有所不同。根據(jù)這具體的Contract,長(zhǎng)方形和正方形可以是子類(lèi)-超類(lèi)、超類(lèi)-子類(lèi)、同屬某超類(lèi)的子類(lèi)(sibling)的關(guān)系。
我的書(shū)中并沒(méi)有講這些,因?yàn)槲也幌牖ㄌ嗟恼鹿?jié)講解DbC。DbC本身就可以寫(xiě)一本書(shū)了。
jeffyan,你說(shuō)java的Object單根違背LSP?這個(gè)說(shuō)法很有趣,不知道根據(jù)是什么?
另外,我不認(rèn)為這個(gè)問(wèn)題和dbc有什么聯(lián)系。它是純粹理論上的類(lèi)型系統(tǒng)協(xié)變?cè)瓌t而已,而dbc則是運(yùn)行時(shí)的,是當(dāng)類(lèi)型系統(tǒng)不夠處理復(fù)雜的約束關(guān)系時(shí)才用到的。
i feel it enough for discussion of relation between rectangle and sqaure. we are now talking in the areas of 程序語(yǔ)義學(xué),代數(shù)邏輯 and/or 知識(shí)構(gòu)造模型, 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.
早上一邊和同事講話(huà)一邊打字,沒(méi)有講清楚。LSP有兩種形式,一種較強(qiáng),一種較弱。我書(shū)中講的就是弱LSP,強(qiáng)LSP要求父類(lèi)和子類(lèi)的接口相同。
Java對(duì)象樹(shù)的單根性違背強(qiáng)LSP。
如果一個(gè)問(wèn)題與LSP有關(guān),就一定與DbC有關(guān)。
google了一下,沒(méi)有發(fā)現(xiàn)“強(qiáng)LSP”的相關(guān)資料。只有一個(gè)簡(jiǎn)單的替換原則的定義:
FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.
從這個(gè)定義出發(fā),Java的Object是完全符合定義的。
這個(gè)替換原則,也就是和OO類(lèi)型理論里面的subsumption定義一樣的了。也就是說(shuō),符合LSP的,才能是subtype。
至于你說(shuō)的父子必須具有相同的接口,恕我魯鈍,不知道這樣要求的意義何在?所謂“強(qiáng)”,又強(qiáng)在何處?能不能給一個(gè)“強(qiáng)LSP”的定義?
還有,網(wǎng)上是有一些文章是和DBC和LSP相關(guān)的,但是,那都是本身在討論DBC,而DBC的基礎(chǔ)就是要符合LSP。所以,要研究Dbc不搞清楚LSP自然是不合理的。
但是,這不能反過(guò)來(lái)說(shuō)要研究LSP就要研究DBC呀。可以說(shuō)LSP是父類(lèi),DBC是子類(lèi)。這個(gè)依賴(lài)關(guān)系不能倒過(guò)來(lái)呀。
這個(gè)問(wèn)題本來(lái)就是只跟LSP相關(guān),是靜態(tài)類(lèi)型領(lǐng)域的,跟動(dòng)態(tài)的DBC的關(guān)系只怕很牽強(qiáng)吧?除非你的"DBC"不是eiffel那種意義上的DBC。
愛(ài)因斯坦說(shuō)我們就像一個(gè)走進(jìn)圖書(shū)館的小孩,仰頭看著高大的書(shū)櫥,知道這些書(shū)一定是按照某種順序排列的,可是我們不知道是什么順序。
在自然科學(xué)的領(lǐng)域,人類(lèi)還處于幼年。
別討論了,說(shuō)句不好聽(tīng)的話(huà),各位對(duì)類(lèi)的概念還沒(méi)完全搞清楚!
那樓上的解釋一下什么是類(lèi)吧。
要不來(lái)個(gè)具體的,解釋一下什么是人類(lèi)?
你還真跟我叫勁了!什么叫解釋一下人類(lèi)!
你把類(lèi)的概念寫(xiě)出來(lái),我一條一條給你解釋?zhuān)?br />
我總覺(jué)得模式很難很難,唯有不斷重構(gòu)
大家好,我就是你們討論的正方形,本來(lái)我和長(zhǎng)方形井水不犯河水,但是你們苦苦相逼,長(zhǎng)的像就一定有關(guān)系嗎,那要看不同的應(yīng)用了
這個(gè)帖子我很久以前留言過(guò),沒(méi)想到爭(zhēng)論到今天還沒(méi)有結(jié)果。
一個(gè)帖了這么久的帖子,我原以為有些高手會(huì)留下他們的評(píng)論,但是很遺憾的,我看到得不多,所以我看到這些爭(zhēng)論的時(shí)候,我想起了一個(gè)名詞,學(xué)院派。OO,LSP,DBC...如果你不拿來(lái)寫(xiě)程序的話(huà),估計(jì)全部的價(jià)值也就是在論壇里面炫耀誰(shuí)看的書(shū)多,誰(shuí)的記性好。
看了大家的帖子,我終于明白了,其實(shí)我們大家都不錯(cuò)了,長(zhǎng)方形和正方形沒(méi)有關(guān)系,如果有也只是兄弟關(guān)系。
有三個(gè)概念:矩形,長(zhǎng)方形,正方形。
矩形是后面二者的父類(lèi),代表了擁有兩對(duì)垂直邊的四邊形
長(zhǎng)方形:兩邊不等的矩形
正方形:兩邊相等的矩形
其中需要提醒的是矩形保證的是有四個(gè)邊兩組,setWidth(),setHeight(),但是他并不假設(shè)二者之間的關(guān)系,二者之間的關(guān)系留給了子類(lèi):一個(gè)相等(正方形),一個(gè)不相等(長(zhǎng)方形)。矩形保證 area == height*width,但是它并不承諾正方形的 area == height*height == width*width,并不承諾長(zhǎng)方形的 height != width ,正方形setHeight()和setWidth()以后area = height*width依然成立,因?yàn)楦割?lèi)并沒(méi)有相關(guān)的承諾:改變width以后還維持原來(lái)的height,
(可惜我的英語(yǔ)不好,不知道怎么用兩個(gè)單詞區(qū)分矩形和長(zhǎng)方形,所以沒(méi)法子寫(xiě)出示例代碼)
集合的包含關(guān)系完全可以表現(xiàn)為繼承上的父子關(guān)系,就象屬于關(guān)系演化為聚合關(guān)系一樣。
但是基于上面的理由,長(zhǎng)方形和正方形沒(méi)有任何關(guān)系,比較長(zhǎng)方形和正方形的多少也是沒(méi)有意義的,就象,你說(shuō)整數(shù)和小數(shù)哪個(gè)多?
我雖然喜歡c++,但是絲毫不影響我認(rèn)為java同樣是一門(mén)出色的語(yǔ)言,特別是對(duì)OO的貫徹上。
建議大家看看一些經(jīng)典的關(guān)于java中 interface 和 abstract class的區(qū)別的討論,也許比討論“白馬非馬”有益得多。
受教受教了。
樓上的意思大概是說(shuō)使用矩形這個(gè)抽象類(lèi)吧。這也是我們討論過(guò)的方案之一。但普遍的看法是
這個(gè)方案其實(shí)與我們的討論沒(méi)有太大關(guān)聯(lián)。
我覺(jué)得如果能為正方形找到合適的setX(),setY()方法,那么這個(gè)問(wèn)題就解決了。
實(shí)際上不難。比如將setX()和setY()設(shè)置為抽象方法,在使用正方形這個(gè)類(lèi)的時(shí)候根據(jù)情況
適當(dāng)加以實(shí)現(xiàn)。(即,如果你不能確定使用者將如何使用,那么干脆留下一個(gè)接口好了,這也
符合開(kāi)閉原則的精神)
所有的罪惡都來(lái)自子類(lèi)的創(chuàng)建者:-),因?yàn)槟銈兏采w不當(dāng)。
如果你們找不到完美的可以確保萬(wàn)無(wú)一失的方法來(lái)覆蓋父類(lèi)中的方法,那么就應(yīng)該把問(wèn)題拋給
你們的下一級(jí)。
反之,你可以確定沒(méi)有問(wèn)題的方法,那你應(yīng)該承擔(dān)起你的責(zé)任和義務(wù)。
PS :
樓上連interface和abstract class都出來(lái)了。那篇討論三個(gè)原則的文章流傳的也真夠廣。
請(qǐng)問(wèn)“承諾”為何物?莫非是asdmonster(asd)兄自創(chuàng)的語(yǔ)言?
請(qǐng)教我怎樣在使用您的基類(lèi)(or接口)時(shí)可以得到您的所謂“承諾”?
我可以負(fù)責(zé)的告訴你,沒(méi)有你所謂的“學(xué)院派”,就不會(huì)有計(jì)算機(jī)。
更不會(huì)有編譯器,數(shù)據(jù)結(jié)構(gòu),操作系統(tǒng)。
現(xiàn)在OO研究中就是缺少數(shù)學(xué)理論的支持。
世界上最有錢(qián)的是商人,最有知識(shí)的是學(xué)者,最有實(shí)踐經(jīng)驗(yàn)的是工程技術(shù)人員。
這三種人對(duì)軟件乃至計(jì)算機(jī)的發(fā)展都是缺一不可的。
Ajoo:
讀一讀Liskov 和 Luttag的書(shū)。LSP本就是我最早引入到中文圖書(shū)中的,當(dāng)初沒(méi)有提出這個(gè)強(qiáng)和弱的概念。
jeff, 給個(gè)書(shū)名或者連接吧。謝了。:)
其實(shí),我是挺期待你能在這里給介紹一下這個(gè)強(qiáng)lsp的概念的。我實(shí)在想不出讓兒子必須和父親一樣有什么意義。能否照你的理解給個(gè)例子?(我發(fā)現(xiàn)例子比直接的定義好理解很多)
我search的可是“strong lsp”,不是“強(qiáng)lsp”,所以應(yīng)該不僅僅是中文圖書(shū)拉。但是仍然是一無(wú)所獲。似乎當(dāng)初Liskov也沒(méi)有提出“強(qiáng)弱”的概念吧?
關(guān)于LSP與DbC的關(guān)系有很多討論,我不想重復(fù)了。最為激進(jìn)的討論認(rèn)為L(zhǎng)SP和DbC是同一個(gè)東西兩個(gè)名字,也就是AKA的關(guān)系;較為中肯的,譬如Uncle Bob認(rèn)為二者closely related。如果你認(rèn)為兩者關(guān)系很大,那你基本上就和大多數(shù)人沒(méi)有什么區(qū)別,我也沒(méi)有什么好問(wèn)的。可如果你認(rèn)為兩者沒(méi)有關(guān)系或者關(guān)系不大,那么你的觀點(diǎn)就較突出,是不是需要說(shuō)明一下理由。
DbC描述Contract下的設(shè)計(jì),因此是靜態(tài)的。一個(gè)Contract包括前條件、后條件和不變量,這三者都應(yīng)當(dāng)在設(shè)計(jì)階段給定,除非軟件需求發(fā)生變化或者進(jìn)行重新設(shè)計(jì),不然是不會(huì)改變的。對(duì)這個(gè)感興趣的朋友可以看看Java iContract插件,它直觀地顯示了DbC的概念。學(xué)習(xí)DbC不需要學(xué)習(xí)eiffel,這是一個(gè)常識(shí)。
一個(gè)方法在被執(zhí)行的時(shí)候,必須滿(mǎn)足前條件,在執(zhí)行之后,結(jié)果必須滿(mǎn)足后條件。假設(shè)基類(lèi)A和子類(lèi)B形成繼承關(guān)系,兩者都有一個(gè)方法m。那么B.m()的前條件必須較A.m()的前條件弱一點(diǎn)(或不變),B.m()的后條件必須A.m()的后條件強(qiáng)一點(diǎn)(或不變)。
以DbC為出發(fā)點(diǎn)分析一下長(zhǎng)方形和正方形的關(guān)系,就會(huì)發(fā)現(xiàn)正方形的后條件比長(zhǎng)方形的要弱。因此在某些情況下,只要能保證二者前條件相同,那么yangye所說(shuō)的就是正確的:長(zhǎng)方形是正方形的子類(lèi);如果不能保證這一點(diǎn),那么二者就不能有繼承關(guān)系,而只能是sibling的關(guān)系了。當(dāng)然,如果一般地表述,使用sibling的關(guān)系總不會(huì)錯(cuò)。
至于LSP的幾種形式,如我前面所述,請(qǐng)讀讀Liskov和Guttag書(shū)中她本人的描述。其他情緒化的問(wèn)題我就不接了。
呵呵,大家討論。
暈,一定要我寫(xiě)成你們熟悉的方式?
整數(shù)是小數(shù)的特例嗎?不是。相等是不等的特例嗎?不是。因此正方形是長(zhǎng)方形的特例嗎?不是。為什么,我不是數(shù)學(xué)系的,我無(wú)法準(zhǔn)確的告訴你在哪年哪月哪個(gè)數(shù)學(xué)家的哪篇文章里面說(shuō)過(guò)。如果你認(rèn)為是而且需要我信服,請(qǐng)給出論證。
同上理,如果認(rèn)為正方形的父類(lèi)是沒(méi)有限制長(zhǎng)寬關(guān)系的矩形而不是已經(jīng)限制了長(zhǎng)寬必須不等的長(zhǎng)方形,并不違反BCP,LSP,說(shuō)到底,正方形和長(zhǎng)方形的父類(lèi)并沒(méi)有這樣的Postconditions:setWidth()(或者setX())以后Height(或者Y)并不改變。但是換句話(huà)說(shuō),一定要硬套,那么,正方形的那四條相等的邊,到底是對(duì)契約的強(qiáng)化還是弱化?都不是,相等既不是不等的強(qiáng)化也不是不等的弱化,二者是兩個(gè)并列的關(guān)系。
我提出interface 和abstract class不過(guò)是覺(jué)得這個(gè)問(wèn)題的討論很適合闡明Object,不是嗎,要是各位都明了了什么是Object,知道什么應(yīng)該extend的,什么才是用來(lái)implement的contract,還用在這里喋喋不休?
當(dāng)然,本人才疏學(xué)淺,自創(chuàng)不出一個(gè)“承諾”來(lái)。
路過(guò),隨便說(shuō)兩句閑話(huà),各位自便吧。
to: jeffyan77
假設(shè)基類(lèi)A和子類(lèi)B形成繼承關(guān)系,兩者都有一個(gè)方法m。那么B.m()的前條件必須較A.m()的前條件弱一點(diǎn)(或不變),B.m()的后條件必須A.m()的后條件強(qiáng)一點(diǎn)(或不變)。
能舉個(gè)例子說(shuō)明一下這句話(huà)嗎?
我不會(huì)對(duì)你的例子進(jìn)行反駁,只要能讓我明白你的意思就可以了。 隨便舉
我不反對(duì)別人反駁我的意見(jiàn),我只是不想被別人拖入到我不想卷入的辯論中去。譬如我本來(lái)也不想在這個(gè)地方介紹DbC,可現(xiàn)在身不由己。好吧,下面沿用你的代碼例子
下面是你的長(zhǎng)方形:
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
這就是說(shuō)"正方形的后條件比長(zhǎng)方形的要弱"。
有朋友會(huì)問(wèn),正方形的后條件是不是有兩條
(1)length=新的length
(2)width=新的length
這樣一來(lái)是不是就可以說(shuō)"正方形的后條件比長(zhǎng)方形的要強(qiáng)"了呢?不能,因?yàn)殚L(zhǎng)方形的第二條后條件在這里改掉了。
這個(gè)地方是不是可以使用Venn Diagram?當(dāng)然可以。
可能有朋友會(huì)問(wèn),你是怎樣決定后條件的呢?是不是有隨意性?答案是要根據(jù)對(duì)象的狀態(tài)和使用端的需求。
(1)你這兩個(gè)類(lèi)只有兩個(gè)狀態(tài):長(zhǎng)和寬。
(2)使用端的需求建立在長(zhǎng)和寬基礎(chǔ)之上。
如果使用端不依賴(lài)于寬,那么width屬性沒(méi)有存在的必要。使用端根本不會(huì)發(fā)現(xiàn)setLength()后條件中width的變化,那么這個(gè)后條件就可以去除,這樣一來(lái)長(zhǎng)方形和正方形可以互為基類(lèi),或者根本沒(méi)就必要分成兩個(gè)類(lèi)。或者使用端給出新的需求,并衍生出新的后條件,用來(lái)決定誰(shuí)是基類(lèi)。
繼承關(guān)系必須建立在類(lèi)的行為的基礎(chǔ)之上,而行為的前后條件只能由對(duì)象的狀態(tài)描述。
我們這里討論的氣氛一直很好,比如很少有那種“辯手”,只關(guān)心把別人辨倒。自己沒(méi)有建設(shè)性的東西,只憑一點(diǎn)小機(jī)智、小聰明,專(zhuān)挑別人的細(xì)小破綻。誰(shuí)都不是神仙,這里也不是大辯論論壇,大家討論問(wèn)題也是以弄懂技術(shù)問(wèn)題為目的,沒(méi)有其他目的。遇到這種人我就說(shuō)明我的技術(shù)觀點(diǎn),然后免戰(zhàn)。因?yàn)槲覜](méi)有那種功夫,也沒(méi)有那種腎上腺素,辨倒我也不是什么大獎(jiǎng)。我這只是一般性的說(shuō)說(shuō),我還沒(méi)有發(fā)現(xiàn)我們這里有這種人。
祝大家節(jié)日好!
我一直相信實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)
jeff, 我想我的觀點(diǎn)說(shuō)的很明確了吧?
就是說(shuō)dbc的一些規(guī)則依賴(lài)于lsp。但是lsp并不依賴(lài)于dbc。
lsp僅僅是說(shuō)子類(lèi)的對(duì)象必須能當(dāng)作父類(lèi)使用。并沒(méi)有更多的外延。
而這個(gè)概念應(yīng)用到語(yǔ)言的靜態(tài)類(lèi)型系統(tǒng)上,就是所謂的subsumption和協(xié)變規(guī)則。
應(yīng)用到動(dòng)態(tài)的dbc系統(tǒng)上,就是前置后置條件的強(qiáng)弱關(guān)系。
畫(huà)個(gè)圖呢,就是這樣:
Subsumption--------------->LSP<---------------Dbc
兩者都依賴(lài)LSP。
而我的理解是,我們?cè)谟懻摫緛?lái)沒(méi)有涉及dbc的靜態(tài)的類(lèi)型is-a和LSP的關(guān)系。而這只怕扯不到DBC上去。否則只怕所有的對(duì)類(lèi)型系統(tǒng)的討論都要涉及dbc了,這明顯不符合事實(shí)。除非你所說(shuō)的dbc更為廣義,包括動(dòng)態(tài)的前后置條件,也包括靜態(tài)的類(lèi)型約束。
至于lsp的概念,既然不想介紹就算了,肯花那么大篇幅給truezerg介紹前后置條件的協(xié)變關(guān)系,卻不肯給我介紹一下強(qiáng)lsp,偏心??!:)
不過(guò)我舍不得為了這個(gè)就買(mǎi)本書(shū),找不到網(wǎng)上的資料,只好先這么無(wú)知下去吧。
回復(fù)人: yangye1211(楊楊) ( ) 信譽(yù):100 2003-12-10 01:40:00 得分:0
我一直相信實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)
-----------------------------------------------
呵呵,我看最脫離實(shí)踐的就是你了,正方形應(yīng)該不應(yīng)該從長(zhǎng)方形繼承還是讓他們從一個(gè)共同的抽象類(lèi)來(lái)繼承又或是完全用同一個(gè)類(lèi)來(lái)表示它們,答案只有一個(gè):看實(shí)際情況??次覀冴P(guān)心的是哪個(gè)方面。
分類(lèi)是一門(mén)學(xué)問(wèn),同一頭牛,你是把它分到“家畜”還是“哺乳動(dòng)物”還是“有蹄類(lèi)動(dòng)物”,完全取決于你的具體應(yīng)用,看你關(guān)心的是什么。甚至它有哪些屬性也是從實(shí)際出發(fā)啊。假如我們給奶牛場(chǎng)編一個(gè)“牛”類(lèi),就應(yīng)該有產(chǎn)奶量、年齡之類(lèi)的屬性,但是要是我們給屠宰場(chǎng)作程序呢,產(chǎn)奶量還有意義嗎,這時(shí)候就應(yīng)該是產(chǎn)肉量了吧??傊煌膽?yīng)用關(guān)心的方面不同。
還有你說(shuō):“拜托,不要跟班上的人比(潛臺(tái)詞是其實(shí)班上懂設(shè)計(jì)的有幾個(gè),將來(lái)都是做TechnicalSupport和Marketing的料)”
----------------------------------------------
年輕人牛氣沖天是好事,但不要靠貶低別人來(lái)抬高自己。
長(zhǎng)方形和正方形的問(wèn)題很可能是因?yàn)椤禞ava與模式》而引起如此多的關(guān)注,討論對(duì)于啟發(fā)思考是一件好事情。這個(gè)問(wèn)題在西方也一再引起討論,本身已有公認(rèn)的結(jié)論。結(jié)論我在書(shū)中已經(jīng)講過(guò)了,在上面的帖子里也作了附加的說(shuō)明,不想重復(fù)了。
OO語(yǔ)言設(shè)計(jì)中最為困難的是什么?類(lèi)型。Eiffel語(yǔ)言最早的設(shè)計(jì)就有類(lèi)型設(shè)計(jì)的邏輯錯(cuò)誤。LSP和DbC都是為了給類(lèi)型設(shè)計(jì)提供指導(dǎo)而出現(xiàn)的,它們本來(lái)自于兩個(gè)敵對(duì)的(這個(gè)字眼太強(qiáng)了一點(diǎn))研究組,一個(gè)是Liskov組,一個(gè)是Meyer組。這兩個(gè)組的東西在邏輯上是絕不可能相互依賴(lài)的,DbC依賴(lài)于LSP的可能性是不存在的。
LSP帶有強(qiáng)烈的Defensive Programming的味道,DbC則是Blackbox testing的味道。它們的目標(biāo)相同,結(jié)論如果不是完全相同,肯定也是非常接近,但是尚無(wú)法證明它們是完全等價(jià)的。有一點(diǎn)可以肯定:LSP和DbC都是類(lèi)型設(shè)計(jì)的指導(dǎo)原則,任何時(shí)候只要有超類(lèi)和子類(lèi),就需要考慮LSP和DbC。
LSP的表達(dá)看上去簡(jiǎn)潔,易于理解,DbC則帶有相當(dāng)多的佐料,讓人家以為這是什么動(dòng)物。實(shí)際上透徹理解LSP并不像看上去得那么容易,理解DbC不像看上去那么難。譬如Liskov 和Guttag的書(shū)中花費(fèi)了相當(dāng)?shù)钠v解抽象化、類(lèi)型和LSP。LSP可以分成為三個(gè)規(guī)則:
signature rule
methods rule
properties rule
超類(lèi)型可以分成三種
Complete supertype
Incomplete supertype
以及Snippets
LSP應(yīng)用到不同種的超類(lèi)中去,就有了我所說(shuō)的強(qiáng)弱之分。
LSP可以直接應(yīng)用到測(cè)試中去。LSP的外延很深很廣。
迄今為止,絕大多數(shù)的教材談到LSP基本上都是轉(zhuǎn)述別人,很少有人親自閱讀Liskov和Wing最早的研究論文和后來(lái)的著作,對(duì)于DbC也是類(lèi)似。以訛傳訛,不求甚解。江湖越少,膽子越大。
這個(gè)帖子我不會(huì)再來(lái)了。呵呵,大家討論。
我不同意根據(jù)實(shí)際情況來(lái)選擇解決方法的方案。
因?yàn)槭聦?shí)就是事實(shí),不能因?yàn)槟氵€沒(méi)有了解他,就以錯(cuò)誤來(lái)代替。
其實(shí)OO就是要比較理論化,系統(tǒng)化的研究才行。
有種!
既然如此,我也不來(lái)了
回復(fù)人: yangye1211(楊楊) ( ) 信譽(yù):100 2003-12-10 01:40:00 得分:0
我一直相信實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)
回復(fù)人: yangye1211(楊楊) ( ) 信譽(yù):100 2003-12-10 18:33:00 得分:0
我不同意根據(jù)實(shí)際情況來(lái)選擇解決方法的方案。
因?yàn)槭聦?shí)就是事實(shí),不能因?yàn)槟氵€沒(méi)有了解他,就以錯(cuò)誤來(lái)代替。
其實(shí)OO就是要比較理論化,系統(tǒng)化的研究才行。
------------------------------------
前后矛盾啊老大
這次真的不來(lái)了
搞清楚,一個(gè)是檢驗(yàn)真理,一個(gè)是研究真理
你們都是老大,我不行。
其實(shí)都是高手啊。
每個(gè)人都顧及自己的面子,中國(guó)不要搞研究了。
mark
狂水的一個(gè)貼,在不結(jié)沒(méi)完叻
ajoo: 看你,非說(shuō)偏心吧, 閆博士不來(lái)了。 ^_^
開(kāi)玩笑呢。 DbC與lsp的關(guān)系您現(xiàn)在想法是什么?
別吵了!趕快揭帖!
告訴你們用笛卡兒乘積集來(lái)描述類(lèi),你就知道到底是正方形繼承長(zhǎng)方形,還是馬繼承黑馬!!