軟件設計原則----合成/聚合複用原則(CARP)

“要儘量使用合成/聚合,儘量不要使用繼承。”

陳述:

在一個新的對象裏面使用一些已有的對象,使之成爲新對象的一部分,新對象通過向這些對象的委派達到複用已有功能的目的。

引入:

如我們所知,在面向對象設計裏,不同環境中複用已有設計和實現的基本方法:

  1. 繼承。
  2. 合成/聚合。
1、繼承複用

繼承複用通過擴展一個已有對象的實現來得到新的功能,基類明顯地捕獲共同的屬性和方法,而子類通過增加新的屬性和方法來擴展超類的實現。繼承是類型的複用。

繼承複用的優點:

  • 新的實現較爲容易,因爲超類的大部分功能可通過繼承關係自動進入子類;
  • 修改或擴展繼承而來的實現較爲容易。
繼承複用的缺點:
  • 繼承複用破壞封裝,因爲繼承將超類的實現細節暴露給子類。“白箱”複用;
  • 如果超類的實現發生改變,那麼子類的實現也不得不發生改變。
  • 從超類繼承而來的實現是靜態的,不可能再運行時間內發生改變,因此沒有足夠的靈活性。
2、合成/聚合複用

由於合成/聚合可以將已有的對象納入到新對象中,使之成爲新對象的一部分,因此新的對象可以調用已有對象的功能,

其優點在於:

  • 新對象存取成分對象的唯一方法是通過成分對象的接口;
  • 成分對象的內部細節對新對象不可見。 “黑箱”複用;
  • 該複用支持封裝。
  • 該複用所需的依賴較少。
  • 每一個新的類可將焦點集中在一個任務上。
  • 該複用可在運行時間內動態進行,新對象可動態引用於成分對象類型相同的對象。
 缺點:
  • 通過這種複用建造的系統會有較多的對象需要管理。
  • 爲了能將多個不同的對象作爲組合塊(composition block)來使用,必須仔細地對接口進行定義。 
 要正確的選擇合成/複用和繼承,必須透徹地理解里氏替換原則和Coad法則。

Coad法則由Peter Coad提出,總結了一些什麼時候使用繼承作爲複用工具的條件。 Coad法則:

只有當以下Coad條件全部被滿足時,才應當使用繼承關係:

  1. 子類是超類的一個特殊種類,而不是超類的一個角色。區分“Has-A”和“Is-A”。只有“Is-A”關係才符合繼承關係,“Has-A”關係應當用聚合來描述。 
  2. 永遠不會出現需要將子類換成另外一個類的子類的情況。如果不能肯定將來是否會變成另外一個子類的話,就不要使用繼承。 
  3. 子類具有擴展超類的責任,而不是具有置換掉(override)或註銷掉(Nullify)超類的責任。如果一個子類需要大量的置換掉超類的行爲,那麼這個類就不應該是這個超類的子類。 
  4. 只有在分類學角度上有意義時,纔可以使用繼承。不要從工具類繼承。 
錯誤地使用繼承而不是合成/聚合的一個常見原因是錯誤的把“Has-A”當成了“Is-A”。
“Is-A”代表一個類是另外一個類的一種;
“Has-A”代表一個類是另外一個類的一個角色,而不是另外一個類的特殊種類。 

例如:如果我們把“人”當成一個類,然後把“僱員”,“經理”,“學生”當成是“人”的子類。
錯誤在於把“角色”的等級結構和“人”的等級結構混淆了。“經理”,“僱員”,“學生”是一個人的角色,一個人可以同時擁有上述角色。如果按繼承來設計,那麼如果一個人是僱員,就不可能是經理,也不可能是學生,顯然不合理。
正確的設計是有個抽象類“角色”,“人”可以擁有多個“角色”(聚合),“僱員”,“經理”,“學生”是“角色”的子類。

注意:

里氏替換原則是繼承複用的基礎。
只有兩個類滿足里氏替換原則的時候,纔可能是“Is-A”關係。
如果兩個類是“Has-A”關係而非“Is-A”關係,但是設計成了繼承,那麼肯定違反里氏替換原則。

總結:

  • 組合與繼承都是重要的複用方法
  • 在OO開發的早期,繼承被過度地使用
  • 隨着時間的發展,人們發現優先使用組合可以獲得複用性與簡單性更佳的設計
  • 可以通過繼承,擴充(enlarge)可用的組合類集(the set of composable classes)
  • 組合與繼承可以一起工作
  • 基本法則是:優先使用對象組合,而非(類)繼承

參考資源:

《設計模式:可複用面向對象軟件的基礎》,ERICH GAMMA RICHARD HELM RALPH JOHNSON JOHN VLISSIDES著作,李英軍 馬曉星 蔡敏 劉建中譯,機械工業出版社,2005.6

《敏捷軟件開發:原則、模式與實踐》,Robert C. Martin著,鄧輝譯,清華大學出版社,2003.9

《設計模式解析》,Alan Shalloway等著(徐言聲譯),人民郵電出版社,2006.10








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章