1. 程式人生 > >設計模式之多用組合,少用繼承

設計模式之多用組合,少用繼承

對類的功能的擴充套件,要多用組合,少用繼承。 
對於類的擴充套件,在面向物件的程式設計過程中,我們首先想到的是類的繼承,由子類繼承父類,從而完成了對子類功能的擴充套件。但是,面向物件的原則告訴我們,對類的功能的擴充套件要多用組合,而少用繼承。其中的原因有以下幾點: 
第一、子類對父類的繼承是全部的公有和受保護的繼承,這使得子類可能繼承了對子類無用甚至有害的父類的方法。換句話說,子類只希望繼承父類的一部分方法,怎麼辦? 
第二、實際的物件千變萬化,如果每一類的物件都有他們自己的類,儘管這些類都繼承了他們的父類,但有些時候還是會造成類的無限膨脹。 
第三、 繼承的子類,實際上需要編譯期確定下來,這滿足不了需要在執行內才能確定物件的情況。
而組合卻可以比繼承靈活得多,可以在執行期才決定某個物件。
嗨!光說這麼多一二三有什麼用,我們就是想看看實際情況是不是像上面說的那樣呢?還是來看看實際的例子吧! 
現在我們需要這樣一個HashMap,它除了能按常規的Map那樣取值,如get(Object obj)。還能按位取值,像ArrayList那樣,按存入物件對的先後順序取值。 
對於這樣一個問題,我們首先想到的是做一個類,它繼承了HashMap類,然後用一個ArrayList屬性來儲存存入的key,我們按key的位來取值,程式碼如下: 
  1. publicclass ListMap extends HashMap {  
  2. private List list;  
  3. public ListMap() {  
  4.          super();  
  5.           this.list = new ArrayList();  
  6. }  
  7. public Object put(Object key,Object value)  
  8. {  
  9.          if(list.contains(key))  
  10.           {  
  11.                  list.remove(key);  
  12.           }  
  13.          this.list.add(key);  
  14.           returnsuper.put(key,value);  
  15. }  
  16. public Object getKey(int i)  
  17. {  
  18.           returnthis.list.get(i);  
  19. }  
  20. public Object getValue(int i)  
  21. {  
  22.           returnthis.get(getKey(i));  
  23. }  
  24. publicint size()  
  25. {  
  26.           returnthis.list.size();  
  27. }  
  28. }  
  29. 這個ListMap類對HashMap作了一定的擴充套件,很簡單就實現了上面我們所要求的功能。然後我們對該類做一下測試:  
  30. ListMap map = new ListMap();  
  31.          map.put("a","111");  
  32.          map.put("v","190");  
  33.          map.put("d","132");  
  34.           for(int i=0;i<map.size();i++)  
  35.           {  
  36.                  System.out.println(map.getValue(i));  
  37.           }  
測試結果為: 
111 
190 
132 
正是我們所需要看到的結果。如此說來,這個ListMap類就可以放心的使用了嗎?有實現了這樣功能的類,你的同事或朋友也可能把這個類拿來使用一下,他可能寫出來如下的程式碼: 
  1. ListMap map = new ListMap();  
  2.          map.put("a","111");  
  3.          map.put("v","190");  
  4.          map.put("d","132");  
  5.          String[] list = (String[])map.values().toArray(new String[0]);  
  6.           for(int i=0;i<list.length;i++)  
  7.           {  
  8.                  System.out.println(list[i]);  
  9.           }  
執行的結果如下: 
132 
111 
190 
哎喲,怎麼回事啊?與上面的順序不對了。你朋友過來找你,說你寫的程式碼怎麼不對啊?你很吃驚,說把程式碼給我看看。於是你看到了上面的程式碼。你大罵道,混蛋,怎麼不是用我的getValue方法啊?你朋友搔搔頭道,values方法不是一樣的嗎?你也沒告訴我不能用啊? 
通過上面的例子,我們看到了繼承的第一個危害:繼承不分青紅皁白的把父類的公有和受保護的方法統統繼承下來。如果你的子類沒有對一些方法重寫,就 會對你的子類產生危害。上面的ListMap類,你沒有重寫繼承自HashMap類的values方法,而該方法仍然是按HashMap的方式取值,沒有 先後順序。這時候,如果在ListMap類的物件裡使用該方法取得的值,就沒有實現我們上面的要求。 
接上面的那個例子,你聽了朋友的抱怨,搖搖頭,想想也是,不能怪他。你只得把values方法在ListMap類重寫一遍,然後又嘀咕著,我是不是該把HashMap類的公有方法在ListMap類裡全部重寫?很多方法根本沒有必要用到啊?…… 
對了,很多方法在ListMap里根本不必用到,但是你用繼承的話,還不得不在ListMap裡重寫它們。如果用組合的話,就沒有上面的煩惱了: 
  1. publicclass MyListMap {  
  2. private HashMap map;  
  3. private List list;  
  4. public MyListMap()  
  5. {  
  6.          this.map = new HashMap();  
  7.           this.list = new ArrayList();  
  8. }  
  9. public Object put(Object key,Object value)  
  10. {  
  11.          if(list.contains(key))  
  12.           {  
  13.                  list.remove(key);  
  14.           }  
  15.          this.list.add(key);  
  16.           returnthis.map.put(key,value);  
  17. }  
  18. public Object getKey(int i)  
  19. {  
  20.           returnthis.list.get(i);  
  21. }  
  22. public Object getValue(int i)  
  23. {  
  24.           returnthis.map.get(getKey(i));  
  25. }  
  26. publicint size()  
  27. {  
  28.           returnthis.list.size();  
  29. }  
  30. }  
這樣,你的朋友就只能使用你的getKey和getValue方法了。如果他向你抱怨沒有values方法,你儘可以滿足他的要求,給他新增上那個方法,而不必擔心可能還有方法沒有被重寫了。 
我們來看Adapter模式,該模式的目的十分簡單:我手裡握有一些實現了WhatIHave介面的實現,可我覺得這些實現的功能不夠用,我還需要從Resource類裡取一些功能來為我所用。Adapter模式的解決方法如下: 
  1. publicinterface WhatIHave  
  2. {  
  3.           publicvoid g();  
  4. }  
  5. publicclass Resource  
  6. {  
  7.           publicvoid f()  
  8.           {  
  9.                ……  
  10.           }  
  11.           publicvoid h()  
  12.           {  
  13.                ……  
  14.           }  
  15. }  
上面是兩個基礎類,很明顯,我們所要的類既要有g()方法,也要有f()和h()方法。 
  1. Public class WhatIWant implements WhatIHave  
  2. {  
  3.           private Resource res;  
  4.           public WhatIWant()  
  5.           {  
  6.                  res = new Resource();  
  7. }  
  8. publicvoid g()  
  9. {  
  10.        ……  
  11. }  
  12. publicvoid f()  
  13. {  
  14.          this.res.f();  
  15. }  
  16. publicvoid h()  
  17. {  
  18.          this.res.h();  
  19. }  
  20. }  
上 面就是一個Adapter模式最簡單的解決問題的思路。我們主要到,對於Resource類,該模式使用的是組合,而不是繼承。這樣使用是有多個原因:第 一,Java不支援多重繼承,如果需要使用好幾個不同的Resource類,則繼承解決不了問題。第二,如果Resource類還有一個方法:k(),我 們在WhatIWant類裡使用不上的話,繼承就給我們造成多餘方法的問題了。 
如果說Adapter模式對組合的應用的目的十分簡單明確,那麼Decorator模式對組合的應用簡直就是令人叫絕。 
讓我們還是從Decorator模式的最佳例子說起,咖啡店需要售賣各種各樣的咖啡:黑咖啡、加糖、加冰、加奶、加巧克力等等。顧客要買咖啡,他可以往咖啡任意的一種或幾種產品。 
這個問題一提出來,我們最容易想到的是繼承。比如說加糖咖啡是一種咖啡,滿足ia a的句式,很明顯,加糖咖啡是咖啡的一個子類。於是,我們馬上可以賦之行動。對於咖啡我們做一個咖啡類:Coffee,咖啡加 糖:SugarCoffee,咖啡加冰:IceCoffee,咖啡加奶:MilkCoffee,咖啡加巧克力:ChocolateCoffee,咖啡加糖 加冰:SugarIceCoffee…… 
哎喲,我們發現問題了:這樣下去我們的類好多啊。可是咖啡店的老闆還不放過我們,他又逼著我們增加蒸汽咖啡、加壓咖啡,結果我們發現,每增加一種新的型別,我們的類好像是成幾何級數增加,我們都要瘋了。 
這個例子向我們展示了繼承的第二個缺點,會使得我們的子類快速的膨脹下去,達到驚人的數量。 
怎麼辦?我們的Decorator模式找到了組合來為我們解決問題。下面我們來看看Decorator模式是怎麼來解決這個問題的。 
首先是它們的共同介面: 
  1. package decorator;  
  2. interface Product {  
  3. publicdouble money();  
  4. }  
  5. //咖啡類:
  6. class Coffee implements Product {  
  7. publicdouble money() {  
  8.     

    相關推薦

    設計模式多用組合繼承

    對類的功能的擴充套件,要多用組合,少用繼承。 對於類的擴充套件,在面向物件的程式設計過程中,我們首先想到的是類的繼承,由子類繼承父類,從而完成了對子類功能的擴充套件。但是,面向物件的原則告訴我們,對類的功能的擴充套件要多用組合,而少用繼承。其中的原因有以下幾點: 第一、子類

    組合還是繼承這是一個問題?——由模式談面向物件的原則多用組合繼承

                                                          組合還是繼承,這是一個問題                                              ——由模式談面向物件的原則之多用組合、少用繼承剛剛接觸模式或者學習模式的人,經常

    CSS多個class樣式使用實踐-多用組合繼承

    先用繼承實現一個效果,程式碼如下:<style type="text/css">         .numberList1, .numberList2,  .numberList3, .numberList4{  border:1px solid #ccc;pa

    java 為什麼說多用組合繼承

    對類的功能的擴充套件,要多用組合,少用繼承。組合:新的類由現有物件所組成。繼承:按照現有類的型別來建立新類,無需改變現有類的形式,採用現有類的形式並在其中新增新程式碼。當繼承現有型別時,也就創造新的型別,這個新型別不僅包括現有型別的所有成員(儘管private成員被隱藏起來並

    掛多個css還是新建class-多用組合繼承

    轉載於http://blog.csdn.net/hacke2/article/details/21707133 5. CSS的聚合/組合原則--掛多個class還是新建 CSS裡也包含了設計模式的6大原則,今天講講聚合/組合原則--多用組合,少用繼承 假設有如圖3-

    Java進階篇設計模式六 ----- 組合模式和過濾器模式

    對組 www. 希望 als oid block 個人 定義 lsi 前言 在上一篇中我們學習了結構型模式的外觀模式和裝飾器模式。本篇則來學習下組合模式和過濾器模式。 組合模式 簡介 組合模式是用於把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表

    java設計模式組合模式

    總結: 1.組合模式適用於對樹形結構的操作,比如遞迴檢查每一個許可權樹、遞迴刪除許可權樹等 2.抽象子節點時,比如例子1中 葉子結點(檔案)和非葉子結點(目錄)是有不一致的行為的,比如檔案不能新增目

    設計模式簡單工廠工廠方法抽象工廠模式

    目錄 1.簡單工廠模式 1)最基本的實現         簡單工廠常用的方法就是一個工廠類,裡面包含很多if else結構 或者switch case 、如下程式碼ProductA和ProductB是分別的兩個不同的類: public cl

    設計模式簡單工廠工廠方法模式(c++)

    問題描述 在面向物件系統設計中經常可以遇到以下的兩類問題: 1)為了提高內聚(Cohesion)和鬆耦合(Coupling),我們經常會抽象出一些類的公共介面以形成抽象基類或者介面。這樣我們可以通過宣告一個指向基類的指標來指向實際的子類實現, 達到了多型的目的

    設計模式美學習(八):為何說要多用組合繼承?如何決定該組合還是繼承

    在面向物件程式設計中,有一條非常經典的設計原則,那就是:組合優於繼承,多用組合少用繼承。為什麼不推薦使用繼承?組合相比繼承有哪些優勢?如何判斷該用組合還是繼承? 為什麼不推薦使用繼承? 繼承是面向物件的四大特性之一,用來表示類之間的 is-a 關係,可以解決程式碼複用的問題。雖然繼承有諸多作用,但繼承層次過深

    3、多用字面量語法等價的方法

    keys obj ble alloc ive 3.1 per 無法 bsp 1、字面字符串 摒棄: NSString *someString = [[NSString alloc] initWithString: @"Effective Objective-C 2.0"];

    3.多用字面量語法等價的方法

    使用字面量語法(literal syntax)可以縮減原始碼長度,使其更易讀。 字面數值 有時需要把整數、浮點數、布林值封入 OC 物件中,這種情況下可以用 NSNumber 類,該類可以處理多種型別的數值。 // 不用字面量,建立一個數字 NSNumber *someN

    設計模式介面卡模式(結構型需要一個轉換頭來相容)

    介紹 標準定義:將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 通俗理解:我們日常用的讀卡器就是一個介面卡,記憶體卡不能直接插入筆記

    設計模式委派模式大名鼎鼎的Spring都在

    ### 什麼是委派模式 雖然說委派模式不屬於Gof23中設計模式,但這並不影響它成為一種經典的設計模式。 “委派”字面意思就是指派一件事情給某人。類比到生活中的場景,比如專案leader指派開發任務給下面的猿猿們。這聽起來有點像靜態代理,不過還是不一樣的,你品,你細品!代理強調的是過程,主要是要在代理過程中

    設計模式組合模式

    asp fast 基本 class prototype getch pro 通用 -i 設計模式之組合模式 Oct 19, 2015 組合模式(Composite)將對象組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性。

    漫談設計模式組合模式

    設計模式 組合模式一、什麽是設計模式、為什麽要使用它 對設計模式的解釋有很多,在這裏我按個人的理解闡述一下。設計模式就是一些常見問題的優秀實踐,一套按面向接口嚴格實現的優秀方法,是經過實踐認證的、高效的、解耦的解決方案。那麽為什麽要使用它,一個設計模式定義了一個問題、定義了一個解決方案、是經過測試的、能

    4、多用類型常量#define預處理指令

    報錯 作用域 生成 model 聲明 stat 指令 方法 類常量 摒棄: #define ANIMATION_DURATION 0.3 #define ERROR_MESSAGE @“ErrorMessage” 1)沒有常量的類型信息 2)假設此指令聲明在某個頭文件中

    設計模式八:組合模式(Composite Pattern)

    數據結構 log ide ase 統一 etc 方法 可能 模式 什麽是組合模式呢?簡單來說組合模式就是將對象合成樹形結構以表示“部分整體”的層次結構,組合模式使用戶對單個對象和組合對象使用具有一致性。 組合模式(Composite Pattern)有時

    typescript設計模式組合享元

    abr .html hbm msl gfw targe bgm egg fff js%E4%B8%AD%E7%9A%84%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%85%A5%E9%97%A8 http://music.baidu.co

    第41條:多用派發隊列同步鎖

    線程 ati 之前 lock col class logs pan 同步機制   本條要點:(作者總結)      在 Objective-C 中,如果有多個線程要執行同一份代碼,那麽有時可能會出問題。這種情況下,通常要使用鎖來實現某種同步機制。在 GCD 出現之前,有