設計模式|面向對象設計的七大原則

目錄

綜述

單一職責原則

開閉原則

里氏轉換原則

依賴倒轉原則

接口隔離原則

合成/聚合複用原則

迪米特法則


綜述

  1. 單一職責原則
  2. 開閉原則
  3. 里氏轉換原則
  4. 依賴倒轉原則
  5. 接口隔離原則
  6. 合成/聚合複用原則
  7. 迪米特法則

爲什麼要提倡“Design Pattern呢?根本原因是爲了代碼複用增加可維護性。那麼怎麼才能實現代碼複用呢?

設計模式就是實現了這些原則,從而達到了代碼複用、增加可維護性的目的。

面向對象設計原則爲支持可維護性複用而誕生,這些原則蘊含在很多設計模式中,它們是從許多設計方案中總結出的指導性原則。面向對象設計原則也是我們用於評價一個設計模式的使用效果的重要指標之一,在設計模式的學習中,經常會看到諸如“XXX模式符合XXX原則”、“XXX模式違反了XXX原則”這樣的語句。

單一職責原則要求實現類要職責單一
里氏代換原則要求不要去破壞繼承系統
依賴倒轉原則要求面向接口編程
接口隔離原則要求在設計接口的時候要精簡單一
迪米特法則要求要降低耦合
開閉原則是總綱,要求對擴展開發,對修改關閉

單一職責原則

單一職責原則是最簡單的面向對象設計原則,它用於控制類的粒度大小。單一職責原則定義如下:單一職責原則(Single Responsibility Principle, SRP):一個類只負責一個功能領域中的相應職責,或者可以定義爲:就一個類而言,應該只有一個引起它變化的原因。

單一職責原則是實現高內聚、低耦合的指導方針(軟件工程中對象之間的耦合度就是對象之間的依賴性),它是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,而發現類的多重職責需要設計人員具有較強的分析設計能力和相關實踐經驗。

一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響複用性。例如:要實現邏輯和界面的分離。

開閉原則

開閉原則(Open-Closed Principle, OCP):一個軟件實體應當對擴展開放,對修改關閉。即軟件實體應儘量在不修改原有代碼的情況下進行擴展。

開閉原則明確的告訴我們:軟件實現應該對擴展開放,對修改關閉,其含義是說一個軟件實體應該通過擴張來實現變化, 而不是通過修改已有的代碼來實現變化的。

里氏轉換原則

里氏轉換原則(Liskov Substitution Principle, LSP):所有引用基類(父類)的地方必須能透明地使用其子類的對象。

里氏代換原則告訴我們,在軟件中將一個基類對象替換成它的子類對象,程序將不會產生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類對象的話,那麼它不一定能夠使用基類對象。例如:我喜歡動物,那我一定喜歡狗,因爲狗是動物的子類;但是我喜歡狗,不能據此斷定我喜歡動物,因爲我並不喜歡老鼠,雖然它也是動物。

里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範

在進行設計的時候,儘量從抽象類繼承,而不是從具體類繼承。如果從繼承等級樹來看,所有葉子節點應當是具體類,而所有的樹枝節點應當是抽象類或者接口。當然這個只是一個一般性的指導原則,使用的時候還要具體情況具體分析。

簡單的理解爲一個軟件實體如果使用的是一個父類,那麼一定適用於其子類,而且它察覺不出父類對象和子類對象的區別。也就是說,軟件裏面,把父類都替換成它的子類,程序的行爲沒有變化。

依賴倒轉原則

依賴倒轉原則(Dependency Inversion Principle, DIP):抽象不應該依賴於細節,細節應當依賴於抽象。換言之,要針對接口編程,而不是針對實現編程。

依賴倒轉原則要求我們在程序代碼中傳遞參數時或在關聯關係中,儘量引用層次高的抽象層類,即使用接口和抽象類進行變量類型聲明、參數類型聲明、方法返回類型聲明,以及數據類型的轉換等,而不要用具體類來做這些事情。爲了確保該原則的應用,一個具體類應當只實現接口或抽象類中聲明過的方法,而不要給出多餘的方法,否則將無法調用到在子類中增加的新方法。

在引入抽象層後,系統將具有很好的靈活性,在程序中儘量使用抽象層進行編程,而將具體類寫在配置文件中,這樣一來,如果系統行爲發生變化,只需要對抽象層進行擴展,並修改配置文件,而無須修改原有系統的源代碼,在不修改的情況下來擴展系統的功能,滿足開閉原則的要求。

接口隔離原則

接口隔離原則(Interface Segregation Principle, ISP):使用多個專門的接口,而不使用單一的總接口,即客戶端不應該依賴那些它不需要的接口。

根據接口隔離原則,當一個接口太大時,我們需要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關的方法即可。每一個接口應該承擔一種相對獨立的角色,不幹不該乾的事,該乾的事都要幹。這裏的“接口”往往有兩種不同的含義:一種是指一個類型所具有的方法特徵的集合,僅僅是一種邏輯上的抽象;另外一種是指某種語言具體的“接口”定義,有嚴格的定義和結構。

合成/聚合複用原則

合成複用原則(Composite Reuse Principle, CRP):儘量使用對象組合,而不是繼承來達到複用的目的

合成複用原則就是在一個新的對象裏通過關聯關係(包括組合關係和聚合關係)來使用一些已有的對象,使之成爲新對象的一部分;新對象通過委派調用已有對象的方法達到複用功能的目的。簡言之:複用時要儘量使用組合/聚合關係(關聯關係),少用繼承。

在面向對象設計中,可以通過兩種方法在不同的環境中複用已有的設計和實現,即通過組合/聚合關係或通過繼承。

但首先應該考慮使用組合/聚合組合/聚合可以使系統更加靈活,降低類與類之間的耦合度,一個類的變化對其他類造成的影響相對較少;

其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,降低複雜度,而濫用繼承反而會增加系統構建和維護的難度以及系統的複雜度,因此需要慎重使用繼承複用。

通過繼承來進行復用的主要問題在於繼承複用會破壞系統的封裝性,因爲繼承會將基類的實現細節暴露給子類,由於基類的內部細節通常對子類來說是可見的,所以這種複用又稱“白箱”複用,如果基類發生改變,那麼子類的實現也不得不發生改變;從基類繼承而來的實現是靜態的,不可能在運行時發生改變,沒有足夠的靈活性;而且繼承只能在有限的環境中使用(如類沒有聲明爲不能被繼承)。

迪米特法則

迪米特法則又稱爲最少知識原則(LeastKnowledge Principle, LKP),其定義如下:迪米特法則(Law of Demeter, LoD):一個軟件實體應當儘可能少地與其他實體發生相互作用。

如果一個系統符合迪米特法則,那麼當其中某一個模塊發生修改時,就會盡量少地影響其他模塊,擴展會相對容易,這是對軟件實體之間通信的限制,迪米特法則要求限制軟件實體之間通信的寬度和深度。迪米特法則可降低系統的耦合度,使類與類之間保持鬆散的耦合關係。

迪米特法則還有幾種定義形式,包括:不要和“陌生人”說話、只與你的直接朋友通信等,在迪米特法則中,對於一個對象,其朋友包括以下幾類:

  • 當前對象本身(this);
  • 以參數形式傳入到當前對象方法中的對象;
  • 當前對象的成員對象;
  • 如果當前對象的成員對象是一個集合,那麼集合中的元素也都是朋友
  • 當前對象所創建的對象。

任何一個對象,如果滿足上面的條件之一,就是當前對象的“朋友”,否則就是“陌生人”。在應用迪米特法則時,一個對象只能與直接朋友發生交互,不要與“陌生人”發生直接交互,這樣做可以降低系統的耦合度,一個對象的改變不會給太多其他對象帶來影響。

迪米特法則要求我們在設計系統時,應該儘量減少對象之間的交互,如果兩個對象之間不必彼此直接通信,那麼這兩個對象就不應當發生任何直接的相互作用,如果其中的一個對象需要調用另一個對象的某一個方法的話,可以通過第三者轉發這個調用。簡言之,就是通過引入一個合理的第三者來降低現有對象之間的耦合度。

在將迪米特法則運用到系統設計中時,要注意下面的幾點:

在類的劃分上,應當儘量創建鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類造成太大波及
在類的結構設計上,每一個類都應當儘量降低其成員變量和成員函數的訪問權限
在類的設計上,只要有可能,一個類型應當設計成不變類
在對其他類的引用上,一個對象對其他對象的引用應當降到最低。
 

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