問題是什么
白馬是馬嗎?本來這不應該是個問題,可是到了OO程序員這里,它就成了問題。不過程序員們通常討論的是正方形是否是長方形,這和白馬是否是馬是同一個問題。
問題的起源
這就得先說面向對象了,面向對象編程有一個重要原則是里氏替換原則(LSP,Liskov Substitution Principle)。這個原則很簡單,就是凡是用父類的地方就可以用子類替換之。形象點就是,老子做得,兒子就做得。可是在程序世界里往往能產生讓人困惑的結局。最經典的就是正方形是否應該繼承長方形的問題,由于下面將引用別人的討論,我在這里就不寫具體的代碼了。父類長方形有長和寬兩個屬性,有計算面積的方法:長*寬。子類正方形可以繼承這些,可是它要保證長=寬,于是它在設置寬或長的時候同時修改長或寬以確保長=寬?,F在我把一個正方形的實例a當長方形用,給a設置長是4寬是5,那么計算出來的面積會是多少呢?答案是令人驚訝的25而不是應該的20。這就不符合LSP了。所以,問題產生了,正方形是長方形嗎?
個人觀點
正方形是長方形。不過面向對象里的繼承關系在現實中并不存在?,F實中的父類和子類并不遵循LSP,比如馬可以把毛染黑,而白馬就不行,因為白馬染黑了就不是白馬了,這就不符合LSP。實際上,現實生活中的子類是父類的特殊情況(子集),而OO里面子類是對父類的擴展。而且程序里對某個類的定義常常和現實中不一樣,比如我們定義的長方形真的能嚴格對應現實中的長方形嗎?也就是說此長方形非彼長方形,那么開頭的那個問題其實是人們自己給自己找煩惱?,F實中,老子做得兒子不一定做得,但是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(); //注意,這里體顯代換原則。用戶根本不知道真正的實例是正方形,用戶只知道長方形的事情”。。。。。。。。。。?! ?br />
結果面積得出的是16而不是20。我認為這是很正常的事情,那只能說用戶對正方形的特性還不完全了解。作為子類,肯定有和父類不同的地方。包括方法的重寫等,也是常用的。另外,我記得有個著名的以以Shape和其他形狀的calcArea來說明多態的例子:許多不明形狀的對象放在一個數組中,然后用(Shape) arrShape[i].calcArea,能夠得到相應對象正確的面積。也就是說,你的那個例子,本來就應該這樣。
結果面積得出的是16而不是20。我認為這是很正常的事情,那只能說用戶對正方形的特性還不完全了解。作為子類,肯定有和父類不同的地方。包括方法的重寫等,也是常用的。
------------------------------------------------------------------------------
你所說的用戶對正方形的特性還不過完全了解。這句話不對。 首先如果把正方形當成長正形的子類的話,用戶沒有義務去了解正方形。因為它們是拿它當長方形來用的。你不可能讓用戶去了解所有的子類。如果每個子類用戶都必需要了解它們的特性,必需要撐握如何使用它,那就失去了多態性了。因為每當使用一個新的子類去換掉父類就得對這個子類的特別情況加以處理,這非常不合理。
方法重寫的問題:方法可以重寫,但要符合父類限定的條件。在這里,長方形求面積的方法前提規定的是長與寬的積。如果你重寫了這個方法但沒有符合這個前提,程序倒是可以編譯通過,但不符合代替原則。所以出現這種情況有種暗示你的類結構有點問題。(當然如果你改變了前提條件的話或許有可能,比如改變長方形求面積的規則)
Shape類的問題:
對于長方形繼承Shape類的這種情況,和正方形繼承長方形有所不同。 因為Shape類沒有一個求面積的方法(沒有實現)。所以就沒有一個前提規定該如何求。所以長方形當然可以自己實現如何求。沒有任何限制。但正方形繼承長方形以后就受到長方形的限制了。
另外,如果我們找到了Shape類的統一的求面積的方法。比如用微積分來求。那長方形繼承Shape也是正確的。 因為用微積分的方法來得到長方形的面積,得到的也是一個正確的結果。長方形并沒有違反父類的規則,同時任何圖形都可以通過微積分來求得面積,而且都是正確的,所以用戶根本不用考慮給我的是什么圖形,根本不用考慮這個圖形的特殊點。只要通過父類的求面積的方法就可以得到正確的結果。
我覺得無論正方型作為長方形的子類或者長方形作為正方形的子類都有不妥的地方。它們有很多共同的地方也有很多不同的地方??梢栽O計一個抽象的“矩形”類,長方形和正方型都繼承它。所有長方形和正方形的共同特性在父類“矩形類”中實現,所有不同的特性在子類中實現。這樣不會影響長方形和正方型的內部實現邏輯,而且可以保證最大程度的功能復用。
我覺得還是把長方形看成是類,正方形看成是長方形類的一個實例比較合適。
閻博士你好,沒想到能遇見你,很高興,同時想請教關于您書上的一個問題。和上面的兄弟一樣,我也覺得黑馬并非是馬的一個子類。當然,黑馬“是”馬沒有錯誤,我只是覺得所謂“黑”只是馬的一個屬性,所有馬都有顏色這個屬性。黑或白只是屬性的值不同。 不知道為什么黑馬和白馬要成為馬的子類。這樣做有什么有什么實踐價值?
麻煩了閻博士,請您再說明白一下。謝
所有的繼承關系都可以改為委派關系,所有的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()已經修改了父類的方法!?。?br />
抽象不是空中樓閣,不是哲學上討論白馬非馬的問題,而是為了讓你的設計更清晰的體現現實生活中的對象實體,如果沒有現實條件的依托,你的抽象還有什么意味?(說句不中聽的話,與其看一些夸夸其談的東西,不如回頭看看哲學)
在一本書看到的一個觀點是:如果你的抽象繼承的層次超過了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驗證方法來模擬類條件,并加到構造方法中去。凡是符合這個條件的就是子類。這樣不能解決大部分問題,但是應該比一點條件限制也沒有好,至少在程序的抽象世界里會比較有效。
象正方形是否是長方形地問題。只需符合這個長方形地條件就可以了。有的方法失效是在現實中是正?,F象。多態的問題,只好先驗證一下,子類也做點提示告訴用戶。
其實,說實話,這個問題挺無聊的
首先是我的題外話。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在英文里有“擴充, 延伸, 伸展, 擴大”的意思?;氐介L方形和正方形的問題上,認為正方形是長方形子類的同學可以考察一下正方形這個類的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年以上的,做的肯定比你好,有點追求啊。我和他說,要實踐才能出真知?,F在人類剛剛學會造房子,絕大部分人都在造方方正正的那種房子。方方正正的房子誰不會設計,只要是個工頭他就會設計。你說你能設計悉尼歌劇院,一是沒有哪家公司有那么多高智商的建筑工有本事去建,二是還沒有這種需求。既然大家都是四四方方的房子,那大家也都覺得挺滿意。(第一個原因是主要的,因為生產力決定生產關系嘛,等能建這種房子的公司多了,自然會有富人想住設計超前的房子來滿足自己對優越感的追求的)所以我說,設計睡了。你還是多實踐實踐啊。他的回答是,我不需要實踐,只要項目擺在我面前我就能做,而且我對做項目也不感興趣,我的目標是分析到中層。這點我相信。Gates現在讓我設計下一代操作系統我也能啊,支持B2D(Business to desktop),支持超級代碼托管(在某些情況下,根本就不生成代碼),支持SSL3.x with RWPI(實世界個人身份確認),強不?肯定強,Gates也要說我強,但是我要100年才能完成中層設計,Bill一定會將美國國罵脫口而出。
后來我就把這個帖子的事情說給他聽,他脫口而出就是應該把正方形和長方形看成一個類,然后又舉了MFC做例子。小子好樣的,證明我沒看錯人(雖然離我還有點差距,我還知道DNA哈哈哈哈)。然后他的感慨就是不能為了繼承而繼承,很多人現在是輕設計,重coding,我也有同感,其實面向對象啊,數據抽象才是靈魂。就像一個人來講,他的DNA決定了他是他,決定了他哪里多長一塊肉,哪里少長一塊肉。他是誰,他屬于哪一類,并不是由他的行為,他做了什么事情來決定的。差距?。ㄖ劣谡l和誰的差距,見上面那個“吃飯”的典故)。然后又討論了一點其他話題。
今天和惠豬的討論比較簡短,這家伙和我們倆個完全相反,整天埋頭做PHP項目。票子賺了不少,可是我覺得他應該多多參加我們的討論,畢竟他對j2ee的理解還停留在jsp階段。但是我覺得他比我們倆個在有些方面都強,至少有實踐機會,我很多東西都要問他。我就一直想找個實踐機會以后面試問起來也光彩點,怎么說也算個工作經驗啊?,F在有些人用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在英文里有“擴充, 延伸, 伸展, 擴大”的意思?;氐介L方形和正方形的問題上,認為正方形是長方形子類的同學可以考察一下正方形這個類的DNA(開玩笑),正方形extends了長方形沒有,顯然沒有。正方形is-a長方形,沒錯,難道不應該繼承嗎。注意,我們在討論類,沒有討論實例。java教科書中對類的定義大致為:
。。。。。。。。。。。。。。。。。。。。
就extends一詞的理解,我想你把它當成“include”了
“延伸”,我認為是extend最確切的涵義,何為延伸?起碼得保證本體吧,如果本體的性質都不符合,那叫什么延伸?
。。。。
正方形extends了長方形沒有
。。。。
把這里的長方形說成是矩形,應該正規一些。
正方形就是在符合長方形的所有共性的基礎上,多了一個長和寬保持相等的特性。這才是所謂“擴展”,我認為這正是"extends".
剛發那貼的時候,我只能理解到這個程度。
我那貼只是說了點別人還沒看清的問題,但是回避了我看不清的問題。
現在我發現哈哈,等同的東西,誰大誰???
你的意思是長方形>=正方形
那我是不是可以從你上面的話里推出 長方形<=正方形
還是那句話,不要用人的行為來給人分類。“它不能進女浴室所以它是男的”,這不是因果顛倒了嗎?
博士和學士的差別就在于博士知道二叉樹和最多有兩個子樹的有序樹之間的區別。
(個人看法,我本科還沒畢業)
就大小問題,我先不作結論,就下面的話:
。。。。。。。。。
那我是不是可以從你上面的話里推出 長方形<=正方形
。。。。。。。。。
怎么推?還請發表高見。
to 自己:
把這里的長方形說成是矩形,應該正規一些。
正方形就是在符合長方形的所有共性的基礎上,多了一個長和寬保持相等的特性。這才是所謂“擴展”,我認為這正是"extends".
所謂言多必失。剛才這段話,現在想起來是有問題的。其實在上面已經討論很多次了,而這句話又走了老路。因為就算是討論數學概念,正方形概念應該小于長方形(矩形)。也就是說,長方形里面包含有正方形這種特例了。正方形再從長方形繼承(或者說擴展extends),看來是不好的了。
我自己現在差不多已經搞清楚了。大家還要不要繼續討論?
沒關系。列寧說你越想駁斥真理就越發現它的正確性。
感覺在解讀哥巴德赫猜想似的,有意思
TO:yangye1211(楊楊)
說實話, 你對extends的理解是有些偏差的?!〗o我的感覺你總是在比較誰大誰小, 繼承關系不是看誰大誰小來決定的?!xtends是擴展,前面我已經說了?!∵@個擴展是指功能上的,行為上的,能力上的,不是你所說的范圍上的,正象 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是無意義的。
在設計中,我們設計長方形和正方形的時候肯定是要為這兩個類定義方法和屬性的。我覺得只有在定義了方法和屬性的基礎之上我們才能考慮類之間的關系是不是繼承關系。脫離方法和屬性是沒有意義的
大家指教
大家別討論正方形和長方形的關系了。 單純的討論這個沒有什么意義。
不如大家談談在你們各自的實際項目中是如何處理類之間的關系的??梢耘e實際中的例子。并說說自己的分析過程。 我想了解別人分析的過程比單純的爭論一個結果好得多。
不知道這算不算統一了?
我贊成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。如果你認為兩者關系很大,那你基本上就和大多數人沒有什么區別,我也沒有什么好問的??扇绻阏J為兩者沒有關系或者關系不大,那么你的觀點就較突出,是不是需要說明一下理由。
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
我一直相信實踐是檢驗真理的唯一標準
-----------------------------------------------
呵呵,我看最脫離實踐的就是你了,正方形應該不應該從長方形繼承還是讓他們從一個共同的抽象類來繼承又或是完全用同一個類來表示它們,答案只有一個:看實際情況。看我們關心的是哪個方面。
分類是一門學問,同一頭牛,你是把它分到“家畜”還是“哺乳動物”還是“有蹄類動物”,完全取決于你的具體應用,看你關心的是什么。甚至它有哪些屬性也是從實際出發啊。假如我們給奶牛場編一個“牛”類,就應該有產奶量、年齡之類的屬性,但是要是我們給屠宰場作程序呢,產奶量還有意義嗎,這時候就應該是產肉量了吧??傊煌膽藐P心的方面不同。
還有你說:“拜托,不要跟班上的人比(潛臺詞是其實班上懂設計的有幾個,將來都是做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 閱讀(2142)
評論(2) 編輯 收藏 所屬分類:
Java學習