一些面向對象的設計法則(1)

法則1:優先使用(對象)組合,而非(類)繼承

[ Favor Composition Over Inheritance ]




  • 組合



    1.(對象)組合是一種通過創建一個組合了其它對象的對象,從而獲得新功能的複用方法。

    2.將功能委託給所組合的一個對象,從而獲得新功能。

    3.有些時候也稱之爲"聚合"(aggregation)或"包容"(containment),儘管有些作者對這些術語賦予了專門的含義

    4.例如:

    a.聚合:一個對象擁有另一個對象或對另一個對象負責(即一個對象包含另一個對象或是另一個對象的一部分),並且聚合對象和其所有者具有相同的生命週期。(譯者注:即所謂的"同生共死"關係,可參見GOF的Design
    Patterns: Elements of Reusable Object-Oriented Software的引言部分。)

    b.包容:一種特殊類型的組合,對於其它對象而言,容器中的被包含對象是不可見的,其它對象僅能通過容器對象來訪問被包含對象。(Coad)





    5.包含可以通過以下兩種方式實現:

    a.根據引用(By reference)

    b.根據值(By value)

    6.C++允許根據值或引用來實現包含。

    7.但是在Java中,一切皆爲對象的引用!




  • 組合的優點和缺點






1.優點:

a.容器類僅能通過被包含對象的接口來對其進行訪問。

b."黑盒"複用,因爲被包含對象的內部細節對外是不可見。

c.對裝性好。

d.實現上的相互依賴性比較小。(譯者注:被包含對象與容器對象之間的依賴關係比較少)

e.每一個類只專注於一項任務。

f.通過獲取指向其它的具有相同類型的對象引用,可以在運行期間動態地定義(對象的)組合。




2.缺點:

a.從而導致系統中的對象過多。

b爲了能將多個不同的對象作爲組合塊(composition block)來使用,必須仔細地對接口進行定義。




繼承





1.(類)繼承是一種通過擴展一個已有對象的實現,從而獲得新功能的複用方法。

2.泛化類(超類)可以顯式地捕獲那些公共的屬性和方法。

3.特殊類(子類)則通過附加屬性和方法來進行實現的擴展。





繼承的優點和缺點





1.優點:

a.容易進行新的實現,因爲其大多數可繼承而來。

b.易於修改或擴展那些被複用的實現。

2.缺點:

a.破壞了封裝性,因爲這會將父類的實現細節暴露給子類。

b. "白盒"複用,因爲父類的內部細節對於子類而言通常是可見的。

c.當父類的實現更改時,子類也不得不會隨之更改。

d.從父類繼承來的實現將不能在運行期間進行改變。





Coad規則





僅當下列的所有標準被滿足時,方可使用繼承:

a.子類表達了"是一個…的特殊類型",而非"是一個由…所扮演的角色"。

b子類的一個實例永遠不需要轉化(transmute)爲其它類的一個對象。

c.子類是對其父類的職責(responsibility)進行擴展,而非重寫或廢除(nullify)。

d.子類沒有對那些僅作爲一個工具類(utility class)的功能進行擴展。

e.對於一個位於實際的問題域(Problem Domain)的類而言,其子類特指一種角色(role),交易(transaction)或設備(device)。





繼承/組合示例1







1."是一個…的特殊類型",而非"是一個由…所扮演的角色"

-->失敗。乘客是人所扮演的一種角色。代理人亦然。

2.永遠不需要轉化

-->失敗。隨着時間的發展,一個Person的子類實例可能會從Passenger轉變成Agent,再到Agent Passenger。

3.擴展,而非重寫和廢除

-->通過。

4.不要擴展一個工具類

-->通過。

5.在問題域內,特指一種角色,交易或設備

-->失敗。Person不是一種角色,交易或設備。






繼承並非適用於此處!


使用組合進行挽救!





  • 繼承/組合示例2







1."是一個…的特殊類型",而非"是一個由…所扮演的角色"

-->通過。乘客和代理人都是特殊類型的人所扮演的角色。

2.永遠不需要轉化

-->通過。一個Passenger對象將保持不變;Agent對象亦然。

3.擴展,而非重寫和廢除

-->通過。

4.不要擴展一個工具類

-->通過。

5.在問題域內,特指一種角色,交易或設備

-->通過。PersonRole是一種類型的角色。




繼承適用於此處!




  • 繼承/組合示例3







1."是一個…的特殊類型",而非"是一個由…所扮演的角色"

-->通過。預訂和購買都是一種特殊類型的交易。

2.永遠不需要轉化

-->通過。一個Reservation對象將保持不變;Purchase對象亦然。

3.擴展,而非重寫和廢除

-->通過。

4.不要擴展一個工具類

-->通過。

5.在問題域內,特指一種角色,交易或設備

-->通過。是一種交易。




繼承適用於此處!




  • 繼承/組合示例4







1."是一個…的特殊類型",而非"是一個由…所扮演的角色"

-->失敗。預訂不是一種特殊類型的observable。

2.永遠不需要轉化

-->通過。一個Reservation對象將保持不變。

3.擴展,而非重寫和廢除

-->通過。

4.不要擴展一個工具類

-->失敗。Observable就是一個工具類。

5.在問題域內,特指一種角色,交易或設備

-->不適用。Observable是一個工具類,並非一個問題域的類。。




繼承並非適用於此處!




  • 繼承/組合總結





    1.組合與繼承都是重要的重用方法

    2.在OO開發的早期,繼承被過度地使用

    3.隨着時間的發展,我們發現優先使用組合可以獲得重用性與簡單性更佳的設計

    4.當然可以通過繼承,以擴充(enlarge)可用的組合類集(the set of composable classes)。

    5.因此組合與繼承可以一起工作

    6.但是我們的基本法則是:

    優先使用對象組合,而非(類)繼承

    [ Favor Composition Over Inheritance ]
     

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