<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    邊城愚人

    如果我不在邊城,我一定是在前往邊城的路上。

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      31 隨筆 :: 0 文章 :: 96 評(píng)論 :: 0 Trackbacks

    ??? ??? 在匆忙之際理清消除實(shí)現(xiàn)繼承和面向接口編程這樣兩個(gè)大問題可不是一件容易的事情,尤其考慮到自身的認(rèn)識(shí)水平。坦白的說,這又是一篇“炒冷飯”的文章,但這“冷飯”又確實(shí)不好炒。因此,在閱讀了這篇文章之后,你可要批判地接受(拒絕)我的觀點(diǎn),盡管我的觀點(diǎn)也是來自于別人的觀點(diǎn)。

    ??? ??? 繼承是面向?qū)ο笾泻苤匾母拍睢H绻紤]到Java語言特性,繼承分為兩種:接口繼承和實(shí)現(xiàn)繼承。這只是技術(shù)層面的問題,即便C++中不存在接口的概念,但它的虛基類實(shí)際上也相當(dāng)于接口。對(duì)于OO的初學(xué)者來說,他們很希望自己的程序中出現(xiàn)大量的繼承,因?yàn)檫@樣看起來很OO。但濫用繼承會(huì)帶來很多問題,盡管有時(shí)候我們又不得不使用繼承解決問題。

    ??? ??? 相比于接口繼承,實(shí)現(xiàn)繼承的問題要更多,它會(huì)帶來更多的耦合問題。但接口繼承也是有問題的,這是繼承本身的問題。實(shí)現(xiàn)繼承的很多問題出于其自身實(shí)現(xiàn)上,因此這里重點(diǎn)討論實(shí)現(xiàn)繼承的問題。

    ??? ??? 舉個(gè)例子(這個(gè)例子實(shí)在太老套了)。我要實(shí)現(xiàn)一個(gè)Stack類,我想當(dāng)然地選擇Stack類繼承于ArrayList類(你也可以認(rèn)為我很想OO些或者出于本性的懶惰);現(xiàn)在又有了新的需求,需要實(shí)現(xiàn)一個(gè)線程安全的Stack,我又定義了一個(gè)ConcurrentStack類繼承于Stack并覆蓋了Stack中的部分代碼。

    ??? ??? 因?yàn)?font face="文鼎PL細(xì)上海宋Uni, serif">Stack繼承于ArrayListStack不得不對(duì)外暴露出ArrayList所有的public方法,即便其中的某些方法對(duì)它可能是不需要的;甚至更糟的是,可能其中的某些方法能改變Stack的狀態(tài),而Stack對(duì)這些改變并不知情,這就會(huì)造成Stack的邏輯錯(cuò)誤。

    ??? ??? 如果我要在ArrayList中添加新的方法,這個(gè)方法就有可能在邏輯上破壞它的派生類StackConcurrentStack。因此在基類(父類)添加方法(修改代碼)時(shí),必須檢查這些修改是否會(huì)對(duì)派生類產(chǎn)生影響;如果產(chǎn)生影響的話,就不得不對(duì)派生類做進(jìn)一步的修改。如果類的繼承體系不是一個(gè)人完成的,或者是修改別人的代碼的情況下,很可能因?yàn)槔^承產(chǎn)生難以覺察的BUG

    ??? ??? 問題還是有的。我們有時(shí)會(huì)見到這樣的基類,它的一些方法只是拋出異常,這意味著如果派生類支持這個(gè)方法就重寫它,否則就如父類一樣拋出異常表明其不支持這個(gè)方法的調(diào)用。我們也能見到它的一個(gè)變種,父類的方法是抽象的,但不是所有的子類都支持這個(gè)方法,不支持的方法就以拋出異常的方式表明立場(chǎng)。這種做法是很不友好和很不安全的,它們只能在運(yùn)行時(shí)被“僥幸捕捉”,而很多漏網(wǎng)的異常方法可能會(huì)在某一天突然出現(xiàn),讓人不知所措。


    ??? ??? 引起上面問題的很重要的原因便是基類和派生類之間的耦合。往往只是對(duì)基類做了小小的改動(dòng),卻不得不重構(gòu)它們的所有的派生類,這就是臭名昭著的“脆弱的基類”問題。由于類之間的關(guān)系是存在的,因此耦合是不可避免的甚至是必要的。但在做OO設(shè)計(jì)時(shí),當(dāng)遇到如基類和派生類之間的強(qiáng)耦合關(guān)系,我們就要思量思量,是否一定需要繼承呢?是否會(huì)有其他的更優(yōu)雅的替代方案呢?如果一定要學(xué)究的話,你會(huì)在很多書中會(huì)看到這樣的原則:如果兩個(gè)類之間是ISA關(guān)系,那么就使用繼承;如果兩個(gè)類之間是HasA的關(guān)系,那么就使用委派。很多時(shí)候這條原則是適用的,但ISA并不能做為使用繼承的絕對(duì)理由。有時(shí)為了消除耦合帶來的問題,使用委派等方法會(huì)更好地封裝實(shí)現(xiàn)細(xì)節(jié)。繼承有時(shí)會(huì)對(duì)外及向下暴露太多的信息,在GOF的設(shè)計(jì)模式中,有很多模式的目的就是為了消除繼承。

    ??? ??? 關(guān)于何時(shí)采用繼承,一個(gè)重要的原則是確定方法是否能夠共享。比如DAO ,可以將通用的CRUD 方法定在一個(gè)抽象DAO 中,具體的DAO 都派生自這個(gè)抽象類。嚴(yán)格的說,抽象DAO 和派生的DAO 實(shí)現(xiàn)并不具有IS A 關(guān)系,我們只是為了避免重復(fù)的方法定義和實(shí)現(xiàn)而作出了這一技術(shù)上的選擇。可以說,使用接口還是抽象類的原則是,如果多個(gè)派生類的方法內(nèi)容沒有共同的地方,就用接口作為抽象;如果 多個(gè)派生類 的方法含有共同的地方,就用抽象類作為抽象。當(dāng)這一原則不適用于接口繼承,如果出現(xiàn)接口繼承,就會(huì)相應(yīng)地有實(shí)現(xiàn)繼承(基類更多的是抽象類)。

    ??? ??? 現(xiàn)在說說面向接口編程。在眾多的敏捷方法中,面向接口編程總是被大師們反復(fù)的強(qiáng)調(diào)。面向接口編程,實(shí)際上是面向抽象編程,將抽象概念和具體實(shí)現(xiàn)相隔離。這一原則使得我們擁有了更高層次的抽象模型,在面對(duì)不斷變更的需求時(shí),只要抽象模型做的好,修改代碼就要容易的多。但面向接口編程不意味著非得一個(gè)接口對(duì)應(yīng)一個(gè)類,過多的不必要的接口也可能帶來更多的工作量和維護(hù)上的困難。

    ??? ??? 相比于繼承,OO中多態(tài)的概念要更重要。一個(gè)接口可以對(duì)應(yīng)多個(gè)實(shí)現(xiàn)類,對(duì)于聲明為接口類型的方法參數(shù)、類的字段,它們要比實(shí)現(xiàn)類更易于擴(kuò)展、穩(wěn)定,這也是多態(tài)的優(yōu)點(diǎn)。假如我以實(shí)現(xiàn)類作為方法參數(shù)定義了一個(gè)方法void doSomething(ArrayList list)但如果領(lǐng)導(dǎo)哪天覺得 ArrayList不如LinkedList更好用,我將不得不將方法重構(gòu)為void doSomething(LinkedList list),相應(yīng)地要在所有調(diào)用此方法的地方修改參數(shù)類型(很遺憾地,我連對(duì)象創(chuàng)建也是采用ArrayList list = new ArrayList()方式,這將大大增加我的修改工作量)。如果領(lǐng)導(dǎo)又覺得用list存儲(chǔ)數(shù)據(jù)不如set好的話,我將再一次重構(gòu)方法,但這一次我變聰明了,我將方法定義為void doSomething(Set set)創(chuàng)建對(duì)象的方式改為Set set = new HashSet()。但這樣仍不夠,如果領(lǐng)導(dǎo)又要求將set改回list怎么辦?所以我應(yīng)該將方法重構(gòu)為void doSomething(Collection collection)Collection的抽象程度最高,更易于替換具體的實(shí)現(xiàn)類。即便需要List或者Set固有的特性,我也可以做向下類型轉(zhuǎn)換解決問題,盡管這樣做并不安全。

    ??? ??? 面向接口編程最重要的價(jià)值在于隱藏實(shí)現(xiàn),將抽象的實(shí)現(xiàn)細(xì)節(jié)封裝起來而不對(duì)外開放,封裝這對(duì)于Java EE 中的分層設(shè)計(jì)和框架設(shè)計(jì)尤其重要。但即便在編程時(shí)使用了接口,我們也需要將接口和實(shí)現(xiàn)對(duì)應(yīng)起來,這就引出如何創(chuàng)建對(duì)象的問題。在創(chuàng)建型設(shè)計(jì)模式中,單例、工廠方法(模板方法)、抽象工廠等模式都是很好的解決辦法。現(xiàn)在流行的控制反轉(zhuǎn)(也叫依賴注入)模式是以聲明的方式將抽象與實(shí)現(xiàn)連接起來,這既減少了單調(diào)的工廠類也更易于單元測(cè)試。

    ??? ??? 做個(gè)總結(jié)吧。盡管我竭力批駁繼承的不好鼓吹接口的好,但這并不是絕對(duì)的。濫用繼承、濫用接口都會(huì)帶來問題。做Java EE開發(fā)的很多朋友抱怨DAOService中一個(gè)接口一個(gè)類的實(shí)現(xiàn)方式,盡管它們似乎看起來已成為業(yè)界的最佳實(shí)踐之一。也許排除掉接口會(huì)使程序更“瘦”一些,但“瘦”并一定就“好”,需要根據(jù)項(xiàng)目的具體情況而定。關(guān)于繼承和接口的最佳實(shí)踐,各位看官還是需要自身的經(jīng)驗(yàn)積累和總結(jié)了。

    posted on 2007-09-03 10:08 kafka0102 閱讀(2711) 評(píng)論(5)  編輯  收藏 所屬分類: OO

    評(píng)論

    # re: 消除實(shí)現(xiàn)繼承和面向接口編程 2007-09-03 11:19 dennis
    傳統(tǒng)的OO教育中一直很強(qiáng)調(diào)繼承、多態(tài),其實(shí)OO中最重要的是封裝的概念,封裝不僅僅是數(shù)據(jù)的封裝(最初級(jí)的封裝),如果將封裝推廣,抽象類或者說基類是對(duì)派生類的封裝(或者說隱藏),組合也是一種封裝,Adapter、state、Facade等模式更是封裝,在多線程編程中,封裝更是異常重要。  回復(fù)  更多評(píng)論
      

    # re: 消除實(shí)現(xiàn)繼承和面向接口編程 2007-09-03 21:35 Matthew Chen
    個(gè)人也覺得oo中會(huì)出現(xiàn)一些問題,這些問題隨著技術(shù)和思想的進(jìn)步日漸凸現(xiàn).

    但就本文的觀點(diǎn),其實(shí)談不上是oo的封裝和繼承帶來的,很多往往是我們?cè)O(shè)計(jì)人員的疏忽.

    比如第一個(gè)stack的例子,ArrayList是明顯的jfc,繼承自它是完全沒有必要的,除非你的類在抽象空間中能夠真正代表ArrayList類的一個(gè)特殊子類集合,面向?qū)ο髢?yōu)先考慮抽象空間的完美而不是效率和實(shí)現(xiàn)空間的簡(jiǎn)便,這是規(guī)則,也是出于ood的要求,在這個(gè)實(shí)例中,明顯應(yīng)該用聚合而不是繼承.

    拋出異常的問題,對(duì)不支持的方法拋出異常過于拙劣了,完全可以在繼承樹的層次上加上抽象類,很好地分叉,而不是依賴有限的異常處理機(jī)制,異常處理在面向?qū)ο髽?gòu)架中算得上相當(dāng)游離的一個(gè)功能了,盡管看似也有類的繼承關(guān)系,但獨(dú)立的捕獲和處理,并不適合作為業(yè)務(wù)邏輯的相關(guān)環(huán)節(jié)實(shí)現(xiàn).

    oo的問題個(gè)人覺得很多在繼承層次的復(fù)雜性,jfc庫(kù)繼承層次過多,導(dǎo)致無法查知當(dāng)前方法的具體調(diào)用棧位置——也就是具體方法的實(shí)現(xiàn)方式往往被復(fù)雜和繁冗的繼承和封裝掩蓋,讓人摸不找頭腦,不知道過程邏輯究竟在何處實(shí)現(xiàn),也就無法最大限度地學(xué)習(xí)已有的類庫(kù)(當(dāng)然,只是學(xué)習(xí)和修改,因?yàn)槭褂檬遣恍枰懒耍吘狗庋b就是為了屏蔽實(shí)現(xiàn)細(xì)節(jié)。)  回復(fù)  更多評(píng)論
      

    # re: 消除實(shí)現(xiàn)繼承和面向接口編程 2007-09-03 22:17 kafka0102
    @Matthew Chen
    是的,對(duì)于繁雜的對(duì)象模型,如果繼承層次太多就會(huì)很難把握具體的調(diào)用,也更難于修改。對(duì)于我舉的例子,也許并不漂亮,這里只是為了說明一下問題。實(shí)際上,我看到很多代碼有在基類拋出異常或子類拋出異常的情況,因?yàn)樽髡呦M橄蟪龅墓膊僮髦皇菍?duì)其某個(gè)或某幾個(gè)子類不適用,而作者因?yàn)閷?shí)際需求又希望將方法作為公共方法。  回復(fù)  更多評(píng)論
      

    # re: 消除實(shí)現(xiàn)繼承和面向接口編程 2007-09-08 10:56 InPractice
    我就很反感一個(gè)接口一個(gè)實(shí)現(xiàn)的方式。當(dāng)然這是以我的項(xiàng)目經(jīng)驗(yàn)為前提的(不排除目光偏狹的可能性)。
    這種預(yù)留的擴(kuò)展余地,即將來可以加入其他的實(shí)現(xiàn)。在我的項(xiàng)目中事實(shí)上成為了“過度工程”的例子。因?yàn)榈浆F(xiàn)在為止還是一個(gè)接口一個(gè)實(shí)現(xiàn)。倒是每次讀代碼是多了一個(gè)間接級(jí)別,麻煩了很多,效率降低。有人可能爭(zhēng)辯說,代碼讀起來麻煩點(diǎn)沒有關(guān)系,設(shè)計(jì)上還是優(yōu)美的。我不同意這種觀點(diǎn),現(xiàn)實(shí)中的代碼讀的次數(shù)遠(yuǎn)遠(yuǎn)超過寫的次數(shù),讀代碼效率降低的影響不是一個(gè)所謂的優(yōu)美的(在我看是華而不實(shí))設(shè)計(jì)可以彌補(bǔ)的。
    在我看來,一個(gè)接口一個(gè)實(shí)現(xiàn), 恰恰是當(dāng)前流行的反模式。盡管可能在某些情況下有其合理性,但多數(shù)是對(duì)“面向接口編程”的濫用。  回復(fù)  更多評(píng)論
      

    # re: 消除實(shí)現(xiàn)繼承和面向接口編程 2008-01-26 22:40 nabie
    繼承還是應(yīng)該以 IS-A 為原則。樓主的例子要實(shí)現(xiàn)一個(gè) Stack 卻用 ArrayList 作為基類就明顯是一個(gè)濫用 OO 特性的例子,因?yàn)?Stack 并非是一種 ArrayList。正因?yàn)槿绱耍艜?huì)有“可能其中的某些方法能改變Stack的狀態(tài),而Stack對(duì)這些改變并不知情”的情況。并非使用了繼承就是 OO,程序語音中的繼承只是為了支持 OO 的繼承概念而提供的。但如果你在使用語音的繼承特性時(shí)并不按照 OO 的概念來使用,則即使你整個(gè)程序都布滿了各種 OO 特有的東西,你的程序也不是一個(gè) OO 的程序。類的體系應(yīng)該是合乎分類的邏輯的。如計(jì)算機(jī)包括個(gè)人電腦、服務(wù)器和大型機(jī)等,所以個(gè)人電腦、服務(wù)器和大型機(jī)等是個(gè)人電腦的子類,而個(gè)人電腦有分為臺(tái)式機(jī)和筆記本,所以臺(tái)式機(jī)和筆記本是個(gè)人電腦的子類,但他們并非是大型機(jī),所以不是大型機(jī)的子類。你不能因?yàn)楝F(xiàn)在已經(jīng)有大型機(jī)這個(gè)類,而臺(tái)式機(jī)、筆記本和大型機(jī)一樣都能計(jì)算,所以把它們作為大型機(jī)的子類,這樣一些對(duì)大型機(jī)的操作當(dāng)然不能運(yùn)用在臺(tái)式機(jī)和筆記本上。而你可以使他們并列,而提取出計(jì)算功能形成一個(gè)叫計(jì)算機(jī)的父類里,然后臺(tái)式機(jī)、筆記本和大型機(jī)都是計(jì)算機(jī)的子類。  回復(fù)  更多評(píng)論
      


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产亚洲精品美女久久久久| 亚洲av色影在线| 日韩精品成人亚洲专区| 免费黄色网址入口| 啦啦啦在线免费视频| 处破痛哭A√18成年片免费| 免费99精品国产自在现线| 国产精品1024永久免费视频| h视频在线观看免费完整版| 亚洲三级在线免费观看| 美女视频黄的全免费视频网站| av无码国产在线看免费网站| 免费影院未满十八勿进网站| 永久免费毛片在线播放| 毛片免费观看网址| 国产一卡二卡≡卡四卡免费乱码| 免费h成人黄漫画嘿咻破解版| 免费人妻av无码专区| 亚洲一区二区视频在线观看| 亚洲精品无码久久久久| 亚洲AV无码不卡在线播放| 亚洲成年人电影网站| 亚洲欧美黑人猛交群| 免费无遮挡无码视频在线观看 | 国产三级免费观看| 亚洲免费在线观看| 亚洲高清国产AV拍精品青青草原| 亚洲伊人tv综合网色| 亚洲av片不卡无码久久| 美女黄色免费网站| 国产色无码精品视频免费| 亚洲一区二区免费视频| 天天摸天天操免费播放小视频| 九月婷婷亚洲综合在线| 亚洲av中文无码乱人伦在线播放 | 亚洲成av人在线视| 亚洲ts人妖网站| 一区二区三区免费在线视频 | 一个人免费观看www视频在线| 国产精品色午夜免费视频| 久久伊人亚洲AV无码网站|