包含(“有一個....”的關系):標志一個類含有一個基本數據元素或對象。
(1)通過包含來實現“有一個/has a”的關系:比如一個雇員“有一個”姓名,“有一個”電話號碼。
(2)在萬不得已時,通過private繼承來實現“有一個”的關系:這么做的主要原因是讓外層的包含類能夠訪問內層被包含類的protected成員函數和數據成員。但是這樣會從一定程度上破壞封裝性。
(3)警惕又超過約7個數據成員的類:研究表明,人們在做其他事情市能記住的離散項目的個數是7+-2,如果一個類包含超過約7個數據成員,請考慮要不要分解為幾個更小的類。如果數據成員都是整形或者字符串這種簡單數據類型,你可以按照7+-2的上限來考慮,反之,如果數據成員都是復雜對象的話,就應按7+-2的下限來考慮。
繼承(“是一個...”的關系):繼承是說一個類是另一個類的一種特化。
(1)用public繼承來實現“是一個....”的關系:決定用繼承一個現有類的方式創建一個新類事,表明這個新的類是現有類一個更為特殊的版本,基類既對派生類將會做什么設定了預期,也對派生類能怎么運作提出了限制。如果派生類不準備完全遵守由基類定義的同一個接口的契約,繼承就是不正確的實現技術了。
(2)要么使用繼承并進行詳細說明,要么就不要用它。
(3)遵循Liskov替換原則:派生類必須能通過基類的接口而被使用,切使用者無需了解兩者之間的差異。換句話說,對于基類中定義的所有子程序,用在它的任何派生類中時的含義都應該是相同的。
(4)確保只繼承需要繼承的部分:派生類可以繼承成員函數的接口和/或實現。
(5)不要“覆蓋”一個不可覆蓋的成員函數。
(6)把公用的接口、數據及操作放到繼承樹中盡可能高的位置:如果發現把一個子程序移到更到的層次后會破壞該層對象的抽象性,就停手。
(7)只有一個實例的類是值得懷疑的:Singleton模式是特殊的。
(8)派生后覆蓋了某個子程序,擔在其中沒做任何操作,這種情況也值得懷疑。舉例:假設你有一個Cat類,他有一個Scratch(抓)成員函數,可是最終你發現有些貓的爪尖沒了,不能抓了,你可能想從Cat類派生一個叫ScratchlessCat(不能抓的貓)的類,然后覆蓋Scratch方法讓它什么都不做。這種方法是不可取的,修正這一問題的位置不在派生類,而是在最初的Cat類中,應該創建一個Claw(爪子)類并讓Cat類包含它,問題的根源在于做了所有貓能抓的假設。
(9)避免讓繼承體系過深:《Object-Oritented Design Hwuristics》作者Arthur Riel建議把繼承層次限制在最多6層之內,Arthur是基于“神奇數字7+-2”這一理論得出這一建議的,但是依經驗而言,大多數人在腦中同時應付超過2到3層繼承時就有麻煩了,用7+-2來限制一個 基類的派生類總數——而不是繼承層次的層數——可能更為合適。
(10)盡量使用多態,避免大量的類型檢查:頻繁重復出現的case語句有時是在暗示,采用繼承可能是種更好的設計選擇。
(11)讓所有數據都是private(而非protected):如果派生類真的需要訪問基類的屬性,就應該提供protected訪問器函數。