1. 程式人生 > 其它 >設計模式之7原則

設計模式之7原則

在軟體開發中,為了提高軟體系統的可維護性和可複用性,增加軟體的可擴充套件性和靈活性,程式設計師要儘量根據一些原則來開發程式,從而提高軟體開發效率、節約軟體開發成本和維護成本。

  我們常見有 7 條原則,設計模式之禪這本書中概括為6原則,我們本部落格就以7原則來進行說明:

  • 開閉原則
  • 單一職責原則
  • 里氏替換原則
  • 依賴倒轉原則
  • 介面隔離原則
  • 迪米特法則
  • 合成複用原則

一、開閉原則 OCP

  開閉原則(Open Closed Principle,OCP)由勃蘭特・梅耶(Bertrand Meyer)提出,他在 1988 年的著作《面向物件軟體構造》(Object Oriented Software Construction)中提出:軟體實體應當對擴充套件開放,對修改關閉(Software entities should be open for extension,but closed for modification),這就是開閉原則的經典定義。

  開閉原則的意思就是說,你設計的時候,時刻要考慮,儘量讓這個類是足夠好,寫好了就不要去修改了,如果新需求來,我們增加一些類就完事了,原來的程式碼能不動則不動。

  這個原則有兩個特性:

  • 一個是說對於擴充套件是開放
  • 一個是說對於更改是封閉

  想要達到這樣的效果,我們需要使用介面和抽象類,如示例:設計模式之開閉原則示例

二、里氏替換原則 LSP

  里氏替換原則(Liskov Substitution Principle,LSP)由麻省理工學院電腦科學實驗室的里斯科夫(Liskov)女士在 1987 年的“面向物件技術的高峰會議”(OOPSLA)上發表的一篇文章《資料抽象和層次》(Data Abstraction and Hierarchy)裡提出來的。里氏替換原則主要闡述了有關繼承的一些原則,也就是什麼時候應該使用繼承,什麼時候不應該使用繼承。強調的是設計和實現要依賴於抽象而非具體;子類只能去擴充套件基類,而不是隱藏或者覆蓋基類。

  里氏替換原則的官方定義

  • 如果對每一個型別為 T1的物件 o1,都有型別為 T2 的物件o2,使得以 T1定義的所有程式 P 在所有的物件 o1 都代換成 o2 時,程式 P 的行為沒有發生變化,那麼型別 T2 是型別 T1 的子型別
  • 所有引用基類的地方必須能透明地使用其子類的物件

  核心表達的是:父類出現的地方子類都可以出現並替換且程式執行不會出錯,如示例:設計模式之里氏替換原則示例

三、依賴倒置原則 DIP

  依賴倒置原則(Dependence Inversion Principle)是面向物件設計原則的一種。依賴倒置原則指的是高層模組(穩定)不應該依賴於低層模組(變化),二者都應該依賴於抽象(穩定)。抽象(穩定)不應該依賴於實現細節(變化),實現細節應該依賴於抽象(穩定)。

  依賴倒置原則是實現開閉原則的重要途徑之一,它降低了客戶與實現模組之間的耦合。

  依賴、倒置原則的作用

  依賴倒置原則的主要作用如下。

  • 依賴倒置原則可以降低類間的耦合性。
  • 依賴倒置原則可以提高系統的穩定性。
  • 依賴倒置原則可以減少並行開發引起的風險。
  • 依賴倒置原則可以提高程式碼的可讀性和可維護性。

  依賴倒置原則的實現方法

  依賴倒置原則的目的是通過要面向介面的程式設計來降低類間的耦合性,所以我們在實際程式設計中只要遵循以下4點,就能在專案中滿足這個規則。

  • 每個類儘量提供介面或抽象類,或者兩者都具備。
  • 變數的宣告型別儘量是介面或者是抽象類。
  • 任何類都不應該從具體類派生。
  • 使用繼承時儘量遵循里氏替換原則。

  依賴倒置原則的核心就是要我們面向介面程式設計,理解了面向介面程式設計,也就理解了依賴倒置。如示例:設計模式之依賴倒置原則示例

四、單一職責原則 SRP

  單一職責原則(Single Responsibility Principle)是面向物件設計原則的一種。單一職責原則是指不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。

  該原則提出物件不應該承擔太多職責,如果一個物件承擔了太多的職責,至少存在以下兩個缺點:

  • 一個職責的變化可能會削弱或者抑制這個類實現其他職責的能力;
  • 當客戶端需要該物件的某一個職責時,不得不將其他不需要的職責全都包含進來,從而造成冗餘程式碼或程式碼的浪費。

  單一職責原則的優點

  單一職責原則的核心就是控制類的粒度大小、將物件解耦、提高其內聚性。如果遵循單一職責原則將有以下優點。

  • 降低類的複雜度。一個類只負責一項職責,其邏輯肯定要比負責多項職責簡單得多。
  • 提高類的可讀性。複雜性降低,自然其可讀性會提高。
  • 提高系統的可維護性。可讀性提高,那自然更容易維護了。
  • 變更引起的風險降低。變更是必然的,如果單一職責原則遵守得好,當修改一個功能時,可以顯著降低對其他功能的影響。

  單一職責原則的實現方法

  單一職責原則是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,再封裝到不同的類或模組中。

五、介面隔離原則 ISP

  介面分離原則(Interface Segregation PrincipleISP)是面向物件設計原則的一種,也叫介面隔離原則。介面分離原則指在設計時採用多個與特定客戶類有關的介面比採用一個通用的介面要好。即,一個類要給多個客戶使用,那麼可以為每個客戶建立一個介面,然後這個類實現所有的介面;而不要只建立一個介面,其中包含所有客戶類需要的方法,然後這個類實現這個介面。

  介面分離原則是什麼

  • 定義一:客戶端不應該依賴它不需要的介面
  • 定義二:類間的依賴關係應該建立在最小的介面上

  也就是說一個類對另一個類的依賴應該建立在最小的介面上,通俗的講就是需要什麼就提供什麼,不需要的就不要提供。介面中的方法應該儘量少,不要使介面過於臃腫,不要有很多不相關的邏輯方法。

  介面分離原則好處

  • 高內聚,低耦合
  • 可讀性高,易於維護

  介面隔離和單一職責區別

  很多人會覺的介面隔離原則跟之前的單一職責原則很相似,其實不然。

  • 單一職責原則原注重的是職責;而介面隔離原則注重對介面依賴的隔離。
  • 單一職責原則主要是約束類,其次才是介面和方法,它針對的是程式中的實現和細節;而介面隔離原則主要約束介面介面,主要針對抽象,針對程式整體框架的構建。

  採用介面隔離原則對介面進行約束時,要注意以下幾點:

  • 介面儘量小,但是要有限度。對介面進行細化可以提高程式設計靈活性是不掙的事實,但是如果過小,則會造成介面數量過多,使設計複雜化。所以一定要適度
  • 為依賴介面的類定製服務,只暴露給呼叫的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模組提供定製服務,才能建立最小的依賴關係
  • 提高內聚,減少對外互動。使介面用最少的方法去完成最多的事情

  運用介面隔離原則,一定要適度,介面設計的過大或過小都不好。如示例:設計模式之介面隔離原則示例

六、迪米特法則 LoD

  迪米特原則(Law of Demeter, LoD)是面向物件設計原則的一種,也叫最少知道原則。迪米特原則是1987年秋天由lan holland在美國東北大學一個叫做迪米特的專案設計提出的,它要求一個物件應該對其他物件有最少的瞭解,所以迪米特法則又叫做最少知識原則

  迪米特原則問題由來

  迪米特原則目的是儘量降低類與類之間的耦合。類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

  迪米特原則優點

  • 使得軟體更好的可維護性與適應性
  • 物件較少依賴其它物件的內部結構,可以改變物件容器(container)而不用改變它的呼叫者(caller)

  迪米特原則通俗的來講,就是一個類對自己依賴的類知道的越少越好。也就是說,對於被依賴的類來說,無論邏輯多麼複雜,都儘量地的將邏輯封裝在類的內部,對外除了提供的public方法,不對外洩漏任何資訊。如示例:設計模式之迪米特法則示例

七、合成複用原則CRP

  合成複用原則(Composite Reuse Principle,CRP)又叫組合/聚合複用原則(Composition/Aggregate Reuse Principle,CARP)。它要求在軟體複用時,要儘量先使用組合或者聚合等關聯關係來實現,其次才考慮使用繼承關係來實現。 

  如果要使用繼承關係,則必須嚴格遵循里氏替換原則。合成複用原則同里氏替換原則相輔相成的,兩者都是開閉原則的具體實現規範。

  什麼是合成複用

  儘量採用組合(contains-a)、聚合(has-a)的方式而不是繼承(is-a)的關係來達到軟體的複用目的。組合/聚合複用原則是通過將已有的物件納入新物件中,作為新物件的成員物件來實現的,新物件可以呼叫已有物件的功能,從而達到複用。 原則是儘量首先使用合成 / 聚合的方式,而不是使用繼承。

  合成和聚合都是關聯的特殊種類。合成是值的聚合(Aggregation by Value),而聚合是引用的聚合(Aggregation by Reference)。都知道,類之間有三種基本關係,分別是:關聯(聚合和組合)、泛化(與繼承同一概念)、依賴。

  關聯關係包括兩種特例:聚合和組合。

  • 聚合,用來表示整體與部分的關係或者 “擁有” 關係。其中,代表部分的物件可能會被代表多個整體的物件所擁有,但是並不一定會隨著整體物件的銷燬而銷燬,部分的生命週期可能會超越整體。好比班級和學生,班級銷燬或解散後學生還是存在的,學生可以繼續存在某個培訓機構或步入社會,生命週期不同於班級甚至大於班級。
  • 合成,用來表示一種強得多的 “擁有” 關係。其中,部分和整體的生命週期是一致的,一個合成的新的物件完全擁有對其組成部分的支配權,包括建立和泯滅。好比人的各個器官組成人一樣,一旦某個器官衰竭,人也不復存在,這是一種 “強” 關聯。

  組合/聚合複用原則好處

  • 可以降低類與類之間的耦合程度
  • 提高了系統的靈活性

  合成複用原則是通過將已有的物件納入新物件中,作為新物件的成員物件來實現的,新物件可以呼叫已有物件的功能,從而達到複用。如示例:設計模式之合成複用原則示例

八、常用設計模式分類

      

  1、建立型模式

  建立型模式的主要關注點是“怎樣建立物件?”,它的主要特點是“將物件的建立與使用分離”。這樣可以降低系統的耦合度,使用者不需要關注物件的建立細節,物件的建立由相關的工廠來完成。就像我們去商場購買商品時,不需要知道商品是怎麼生產出來一樣,因為它們由專門的廠商生產。

  建立型模式分為以下幾種。

  • 單例(Singleton)模式:某個類只能生成一個例項,該類提供了一個全域性訪問點供外部獲取該例項,其拓展是有限多例模式。
  • 原型(Prototype)模式:將一個物件作為原型,通過對其進行復制而克隆出多個和原型類似的新例項。
  • 工廠方法(FactoryMethod)模式:定義一個用於建立產品的介面,由子類決定生產什麼產品。
  • 抽象工廠(AbstractFactory)模式:提供一個建立產品族的介面,其每個子類可以生產一系列相關的產品。
  • 建造者(Builder)模式:將一個複雜物件分解成多個相對簡單的部分,然後根據不同需要分別建立它們,最後構建成該複雜物件。

  以上 5 種建立型模式,除了工廠方法模式屬於類建立型模式,其他的全部屬於物件建立型模式。

  2、結構型模式

  結構型模式描述如何將類或物件按某種佈局組成更大的結構。它分為類結構型模式和物件結構型模式,前者採用繼承機制來組織介面和類,後者釆用組合或聚合來組合物件

  由於組合關係或聚合關係比繼承關係耦合度低,滿足“合成複用原則”,所以物件結構型模式比類結構型模式具有更大的靈活性。結構型模式分為以下 7 種:

  • 代理(Proxy)模式:為某物件提供一種代理以控制對該物件的訪問。即客戶端通過代理間接地訪問該物件,從而限制、增強或修改該物件的一些特性。
  • 介面卡(Adapter)模式:將一個類的介面轉換成客戶希望的另外一個介面,使得原本由於介面不相容而不能一起工作的那些類能一起工作。
  • 橋接(Bridge)模式:將抽象與實現分離,使它們可以獨立變化。它是用組合關係代替繼承關係來實現的,從而降低了抽象和實現這兩個可變維度的耦合度。
  • 裝飾(Decorator)模式:動態地給物件增加一些職責,即增加其額外的功能。
  • 外觀(Facade)模式:為多個複雜的子系統提供一個一致的介面,使這些子系統更加容易被訪問。
  • 享元(Flyweight)模式:運用共享技術來有效地支援大量細粒度物件的複用。
  • 組合(Composite)模式:將物件組合成樹狀層次結構,使使用者對單個物件和組合物件具有一致的訪問性。

  以上 7 種結構型模式,除了介面卡模式分為類結構型模式和物件結構型模式兩種,其他的全部屬於物件結構型模式

  3、行為型模式

  行為型模式用於描述程式在執行時複雜的流程控制,即描述多個類或物件之間怎樣相互協作共同完成單個物件都無法單獨完成的任務,它涉及演算法與物件間職責的分配。

  行為型模式分為類行為模式和物件行為模式,前者採用繼承機制來在類間分派行為,後者採用組合或聚合在物件間分配行為。由於組合關係或聚合關係比繼承關係耦合度低,滿足“合成複用原則”,所以物件行為模式比類行為模式具有更大的靈活性。

  行為型模式是 GoF設計模式中最為龐大的一類,它包含以下 11 種模式。

  • 模板方法(Template Method)模式:定義一個操作中的演算法骨架,將演算法的一些步驟延遲到子類中,使得子類在可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟。
  • 策略(Strategy)模式:定義了一系列演算法,並將每個演算法封裝起來,使它們可以相互替換,且演算法的改變不會影響使用演算法的客戶。
  • 命令(Command)模式:將一個請求封裝為一個物件,使發出請求的責任和執行請求的責任分割開。
  • 職責鏈(Chain of Responsibility)模式:把請求從鏈中的一個物件傳到下一個物件,直到請求被響應為止。通過這種方式去除物件之間的耦合。
  • 狀態(State)模式:允許一個物件在其內部狀態發生改變時改變其行為能力。
  • 觀察者(Observer)模式:多個物件間存在一對多關係,當一個物件發生改變時,把這種改變通知給其他多個物件,從而影響其他物件的行為。
  • 中介者(Mediator)模式:定義一箇中介物件來簡化原有物件之間的互動關係,降低系統中物件間的耦合度,使原有物件之間不必相互瞭解。
  • 迭代器(Iterator)模式:提供一種方法來順序訪問聚合物件中的一系列資料,而不暴露聚合物件的內部表示。
  • 訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者物件訪問。
  • 備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取並儲存一個物件的內部狀態,以便以後恢復它。
  • 直譯器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即直譯器。

  以上 11 種行為型模式,除了模板方法模式和直譯器模式是類行為型模式,其他的全部屬於物件行為型模式。