軟件架構————面向對象中什麼樣的類纔是好的類

類是由一組數據和子程序構成的集合,這些數據和子程序共同擁有一組內聚的、明確定義的職責。類也可以只是由一組子程序構成的集合,這些子程序提供一組內聚的服務,哪怕其中並未涉及公用的數據。成爲高效程序員的一個關鍵就在於,當你開發程序任一部分的代碼時,都能安全地忽視程序中儘可能多的其餘部分。而類就是實現這以目標的首要工具。


類的基礎:抽象數據類型(ADT)

抽象數據類型是指一些數據以及對這些數據所進行的操作的集合。抽象數據類型可以像在現實世界中一樣的操作實體,而不必在低層的實現上擺弄實體。


使用ADT的益處:

1.可以隱藏細節

2.改動不會影響到整個程序

3.讓接口能提供更多信息

4.容易提高性能

5.讓程序的正確性更顯而易見

6.程序更具有自我說明性

7.無須在程序內到處傳遞數據

8.你可以像在世界中那樣操作實體,而不用在底層實現上操作它


ADT和類:


良好的類接口:

創建高質量的類,最重要的一步就是創建一個好的接口。這也包括了一個可以通過接口來展現的合理的抽象,並確保細節仍被隱藏在抽象背後。


好的抽象:

抽象是一種以簡化的形式來看待複雜操作的能力。類的接口爲隱藏在其後的具體實現提供了一種抽象。類的接口應能提供一組明顯相關的子程序。如果類的接口不能展現出一種一致的抽象,因此它的內聚性就很弱。應該把這些子程序重新組織到幾個職能更專一的類裏去,在這些類的接口中提供更好的抽象。


對類的抽象進行評估的方法是基於類所具有的公用子程序所構成的集合——即類的接口。即使類的整體表現出一種良好的抽象,類內部的子程序也就未必就能個個表現出良好的抽象,也同樣要把它們設計得可以表現出很好的抽象。


關於一些創建類的抽象接口的指導建議:

1.類的接口應該展現出一致的抽象層次:對於類來講,類可以看做一種用來實現抽象數據類型的機制。每一個類應該實現一個ADT,並且僅實現這個ADT。如果發現某個類實現了不止一個ADT,或者不能確定究竟它實現了何種ADT,此時就應該把這個類重新組織爲一個或多個定義更加明確的ADT。如果定義混亂,在修改程序時,混雜的抽象層次會讓程序越來越難以理解,整個程序也逐漸變得無法維護。


2.一定要理解類所實現的抽象是什麼:即這個類是處理什麼事務的,或者描述什麼事務的,如果認識不清寫成的類也會混亂。


3.提供成對的服務:大多數操作都有和其相應的、相等的以及相反的操作。但是不要盲目地創建相反操作,但是一定要考慮,看看是否需要。


4.把不相關的信息轉移到其他類中:有時某個類中一半子程序使用着該類的一半數據,而另一半子程序則使用另一半數據。而卻把他們寫在了一起,此時應該將他們分開。


5.儘可能讓接口可編程,而不是表達語義:每個接口都有一個可編程的部分和一個語義組成部分。可編程的部分由接口中的數據類型和其他屬性構成,編譯器能強制性地要求他們。而語義部分則由“本接口將會被怎麼使用”的假定組成,而這些事無法通過編譯器來強制實施的。


6.謹防在修改時破壞接口的抽象


7.不要添加與接口抽象不一致的公用成員:每次向類接口中添加子程序時,要問問:“這個子程序與現有接口所提供的抽象一致嗎?”如果發現不一致,就要換另一種方法來修改,以便能夠保持抽象的完整性。


8.同時考慮抽象性和內聚性:抽象性和內聚性這兩個概念之間的關係非常緊密——一個呈現出良好的抽象的接口通常也有很高的內聚性。而具有很強內聚性的類往往也會呈現爲很好的抽象,儘管這種關係並不如前者那麼強。


良好的封裝:

封裝是一個比抽象更強的概念。抽象通過提供一個可以讓你忽略現實細節的模型來管理複雜度,而封裝性則強制阻止看到細節.


1.儘可能地限制類和成員的可訪問性:讓可訪問性儘可能低是促成封裝的原則之一。

2.不要公開暴露成員數據:暴露成員數據會破壞封裝性,從而限制對這個抽象的控制力。

3.避免把私用的實現細節放入類的接口中:做到真正的封裝以後,這樣是根本看不到任何實現細節的。

4.不要對類的使用者做出任何假設:類的設計和實現應該符合在類的接口中所隱含的契約。

5.避免使用友元類:在一般情況下友元類會破壞封裝性,因爲它讓你在同一時刻需要考慮更多的代碼量,從而增加了複雜度。

6.不要因爲一個子程序裏僅使用公用子程序,就把它歸入公開接口:何時將接口公開應該考慮一下這個接口所展示的抽象是否一致的。

7.讓閱讀代碼比編寫代碼更方便:爲了讓編寫代碼更方便而降低代碼的可讀性是非常不經濟的。尤其是在創建類的接口時,即時某個子程序與接口的抽象很不相配,有時人們也往往把這個子程序加到接口裏,從而讓這在開發的這個類的某處調用代碼能更方便地使用它。然而,這段子程序的添加正是代碼走下坡路的開始,所以還是不要走出這一步爲好。

8.要格外警惕從語義上破壞封裝性:每當發現自己是通過查看類的內部實現來得知該如何使用這個類的時候,你就不是在針對接口編程了,而是在透過接口針對內部實現編程了。如果透過接口來編程的話,封裝性就被破壞了,而一旦封裝性開始遭到破壞,抽象能力也就快遭殃了。(不要看內部實現細節,應該直接調用抽象的實現函數來處理數據)

9.留意過於緊密的耦合關係:”耦合“是指兩個類之間關聯的緊密程度。通常這種關係越鬆越好,通過以下方式可以做到:儘可能地限制類和成員的可訪問性;避免友元類;在基類中吧數據聲明爲private而不是protected,以降低派生類和基類之間的耦合程度;避免在類的公開接口中暴露成員數據;要對從語義上破壞封裝性保持警惕;


有關設計和實現的問題:

包含(“有一個……”的關係),他表示一個類含有一個基本數據元素或對象。

1.通過包含來實現“有一個/has a” 的關係。

2.在萬不得已時通過private繼承來實現“有一個”的關係

3.警惕有超過7個數據成員的類


繼承(“是一個……”關係)繼承的概念是說一個類是另一個類的一種特化。繼承的目的在於,通過“定義能爲兩個或更多個派生類提供共有元素的基類“的方式寫出來精簡的代碼。

使用繼承的時候注意:

1.對於每一個成員函數而言,它應該對派生類可見嗎?他應該有默認的實現嗎?這一默認的實現能被覆蓋嗎?

2.對於每一個數據成員而言,他應該對派生類可見嗎?


如何考慮上述問題?

1.用public繼承來實現”是一個……“的關係:如果派生類不準備完全遵守有基類定義的同一個接口契約,繼承就不是正確的實現,請考慮用包含的方式,或者對繼承體系上層做修改。

2.要麼使用階乘並進行詳細描述,要麼就不用它

3.遵循Liskov替換原則:除非派生類真的”是一個“更特殊的基類,否則不應該從基類繼承,派生類必須能通過基類的接口而被使用,且使用者無需瞭解兩者之間的差異。

4.確保只繼承需要繼承的部分:派生類可以繼承成員函數的接口和/或實現。

繼承而來的子程序有三種情況:

a.抽象且可覆蓋的子程序是指派生類只繼承了該子程序的程序的接口,但不繼承其實現。

b.可覆蓋的子程序是指派生類繼承了該子程序的接口及其默認實現,並且可以覆蓋該默認實現。

c.不可覆蓋的子程序是指派生類繼承了該子程序的接口及其默認實現,但不能覆蓋該默認實現。

5.不要”覆蓋“一個不可覆蓋的成員函數:如果一個成員函數在基類中是私用的話,其派生類可以創建一個同名的成員函數。對於閱讀派生類代碼的人員來說,這個函數是令人困惑的,因爲他看上去似乎應該是多態的,但事實上卻非如此,只是同名而已。(派生類中的成員函數不要與基類中不可覆蓋的成員函數的重名

6.把公用的接口、數據及操作放到繼承樹中儘可能高的位置:接口、數據和操作在繼承體系中的位置越高,派生類使用它們的時候就越容易。

7.只有一個實例的類是值得懷疑的

8.只有一個派生類的基類也值得懷疑:爲未來要做的工作着手進行準備的最好方法,並不是去創建階層額外的、”沒準哪天能用的上的“基類,而是讓眼下的工作成果儘可能地清晰、簡單、直截了當。

9.派生後覆蓋了某個子程序,但在其中沒做任何操作,這種情況也值得懷疑:這表明基類設計的有問題,應該從基類着手修改它。

10.避免讓繼承體系過深

11.儘量使用多態,避免大量的類型檢查:頻繁重複出現的case語句有時是在暗示,採用繼承可能是種更好的設計選擇——儘管並不總是如此。

12.讓所有數據都是private(而非protected)”繼承會破壞封裝“


繼承規則:

何時使用繼承,何時又該使用包含:

1.多個類共享數據而非行爲,應該創建這些類可以包含的公用對象。

2.多個類共享行爲而非數據,應該讓他們共同的繼承而來,並在基類裏定義公用的子程序。

3.多個類既共享數據也共享行爲,應該讓他們從一個共同的基類繼承而來,並在基類裏定義共同的數據和子程序。

4.當想由基類控制接口時,使用繼承;想要自己控制接口時,使用包含。


成員函數和數據成員:

1.讓類中子程序的數量儘可能少

2.禁止隱式地產生你不需要的成員函數和運算符

3.減少類所調用的不同子程序的數量

4.對其他類的子程序的見解調用要儘可能少

5.一般來說,應儘量減小類和類之間的相互合作的範圍。


構造函數:

1.如果可能,應該在所有的構造函數中初始化所有的數據成員。

2.用私用構造函數來強制實現單件屬性

3.優先採用深層複本,除非論證可行,才採用淺層複本


在面臨深層拷貝還是淺層拷貝時,一種合理的方式是優先實現深層拷貝。


創建類的原因:

1.爲顯示世界中的對象建模

2.爲抽象的對象建模

3.降低複雜度

4.隔離複雜度

5.隱藏實現細節

6.限制變動的影響範圍

7.隱藏全局數據

8.讓參數傳遞更順暢

9.建立中心控制點

10.讓代碼更易於重用

11.爲程序族做計劃


應該避免的類:

1.創建萬能類

2.消除無關緊要的類

3.避免用動詞命名的類

發佈了86 篇原創文章 · 獲贊 10 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章