PHP 設計模式

caoziang發表於2020-02-27

單一職責原則(Single Responsibility Principle)

該原則是針對類來說的,即一個類應該只負責一項職責。假設有一個部門的類叫做T,他的下面有兩個職責的方法叫做P1,P2。假如P1的職責發生改變時去修改這個部門類T,那麼有可能造成職責P2發生故障。

舉個栗子

我們用動物呼吸的場景來表現一下

php 設計模式

輸出結果:

php 設計模式

但是呢,我們發現並不是所有的動物都是呼吸空氣的,比如說魚它是呼吸水的。根據SRP原則,我們應該將Animal類分為陸地動物和海洋生物,如下所示:

php 設計模式

但是我們發現這樣修改花銷很大,既要將原來的類分解,又要修改客戶端。而直接修改Animal類則違背了單一職責原則,但花銷很小 如下所示:

php 設計模式

這種修改方式沒有改變原來的方法,而是在類中新加了一個方法,這樣雖然違反了單一職責原則,但是在方法級別上卻是符合單一職責原則的。在實際的程式設計中,只有邏輯足夠簡單,才可能在程式碼級違反單一職責原則;只有類中的方法數量足夠少,才可以在方法級別上違反單一職責原則。

遵循單一職責的優點:

  • 降低類的複雜度,一個類只負責一項職責。
  • 提高類的可讀性,可維護性。
  • 降低變更引起的風險。

里氏替換原則(Liskov Substitution Principle)

該原則提出,如果對每個型別為T1的物件o1,都有型別為T2的物件o2,使得以T1定義的所有程式P在所有的物件o1都代換成o2時,程式P的行為沒有發生變化,那麼型別T2是型別T1的子型別。這話原句,不知道是翻譯的鍋還是咋地,看起來就晦澀難懂。其實可以簡單地理解為所有引用基類的地方必須能夠透明的使用其子類的物件,在子類中儘量不要重寫和過載父類的方法。

繼承作為物件導向的三大特性之一,在給程式帶來巨大便利的同時,也帶來了弊端。比如繼承會給程式帶來可入侵行,程式的可移植性降低,增加了物件間的耦合性。如果一個類被其他類所繼承,那麼這個類在被修改的時候,必須考慮到所有的子類。並且父類在修改後,所以涉及到子類的功能都有可能發生故障。

舉個栗子

php 設計模式

執行結果:

php 設計模式

後來呢,我們想做個功能,將兩個數相加並且乘以100.這個時候我們看到上面那個類也是兩個引數,只不過是相減。我們繼承一下A重寫下那個方法不就完成求和再求積嗎?程式碼如下:

php 設計模式

執行結果:

php 設計模式

結果我們發現,在業務邏輯程式碼沒變的情況下結果居然跟預期的結果不一樣了。因為C類雖然繼承了A類,但是它重寫了A類的subtract方法,造成了原有功能的錯誤。在實際的編碼過程中我們通常會重寫父類的方法來完成新的功能,但是這樣會使得類的繼承體系複用性特別差。這個時候我們可以選擇讓A和C共同繼承一個更通俗的基類,然後實現他的方法,去掉A和C的繼承關係,採用依賴、聚合、組合等關係代替。舉個栗子:

php 設計模式

php 設計模式

這樣我們既可以保持原有的業務關係,又可以實現更多的功能。

依賴倒轉原則(Dependence Inversion Principle)

依賴倒置規定:高層模組不應該依賴於低層模組,二者都應該依賴其抽象;抽象不應該依賴於細節,細節應該依賴於抽象。因為相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建的架構要比以細節為基礎的架構要穩定的多。依賴倒置的中心思想是面向介面程式設計。上層模組不應該依賴於下層模組,應該依賴於介面。從而使得下層模組依賴於上層的介面,降低耦合度,提高系統的彈性。這六大原則是最虛,最抽象的,很難理解。

舉個栗子

php 設計模式
php 設計模式

但是如果我們讀的是報紙,雜誌呢,發現book並不適用了。我們引入一個抽象的介面IReader,代表讀物。讓Mother類與介面IReader發生依賴關係,而Book和Newspaper都屬於讀物的範疇,讓他們各自都去實現IReader介面,這樣就符合高層不應該依賴低層,應該依賴於介面的依賴倒置原則,修改後程式碼如下:

php 設計模式

php 設計模式

用了依賴倒置原則之後會發現帶給我們極大的便利,比如例子,一開始Mother類與Book類耦合,如果要修改讀物的話,必須要建立一個新的讀物類,然後修改Mother類中傳入的類名,非常的麻煩。而修改後Mother類則直接依賴了IReader介面,這樣與Book解耦,每次只需要建立一個新的讀物類實現IReader介面即可呼叫

依賴關係的傳遞有三種辦法,分別是介面傳遞、構造方法傳遞以及setter方法傳遞。

1.介面傳遞

php 設計模式

2.構造方法傳入(常用)

php 設計模式

3.setter方法傳遞

php 設計模式

在實際的程式設計中儘量注意以下三點:1.低層模組儘量都要有抽象類或者介面類,或者兩者都有2.變數的宣告型別儘量是抽象類或者介面(這裡是指傳入的那個變數代表的類)3.遵循里氏替換原則。控制反轉(IOC)和依賴注入(DI)也是基於此原則,把所有類的例項化放在了一個容器中,從容器中獲取呼叫,降低了高層類對低層類的依賴。

介面隔離原則(InterfaceSegregation Principles)

一個類不應該依賴他不需要的介面;一個類對另一個類的依賴應該建立在最小介面上。比如類A通過介面E依賴類B,類C通過介面E依賴類D,如果介面E對於類A和類C來說不是最小介面的話,則類B和類D必須去實現他們不需要的方法。這個時候我們將臃腫的介面拆分成獨立的幾個介面,類A和類C分別與他們需要的介面建立依賴關係。這就是介面隔離原則。

舉個栗子

php 設計模式

php 設計模式

php 設計模式

php 設計模式

可以看出,介面中出現的方法,不管對依賴於它的類有沒有作用,實現類都必須實現這些方法。這個時候我們把介面拆分下,實現介面隔離原則。舉個栗子:

php 設計模式

php 設計模式

看到這裡,大家可能會覺得介面隔離原則和單一職責原則很相似。其實不是的,1.單一職責原則是注重的這個類的職責,而介面隔離原則注重對介面依賴的隔離2.單一職責約束的是類,其次是方法,針對的是程式中的實現和細節,而介面隔離原則約束的是介面,是抽象,是程式框架整體的構建。

5.迪米特原則(Law of Demete)

個物件應該對其他物件保持最少的瞭解。類與類之間的關係越密切,耦合度越大。迪米特原則又叫最少知道原則,即一個類對自己依賴的類知道的越少越好。也就是說,無論被依賴的類多麼複雜,都儘量將邏輯封裝在類的內部。對外只提供public方法,而不對外洩露任何資訊。迪米特原則還有個更簡單的定義:只與直接的朋友通訊。什麼是直接的朋友:每個物件都會與其他物件有耦合關係,只要兩個物件之間有耦合關係,我們就稱這兩個物件之間是朋友關係。耦合的方式很多,依賴,關聯,組合,聚合等。其中,我們稱出現成員變數,方法引數,方法返回值中的類為直接的朋友,而出現在區域性變數中的類不是直接的朋友。也就是說,陌生的類最好不要以區域性變數的形式出現在類的內部。

舉個栗子:

php 設計模式

這個設計的問題在於CompanyManager中,SubCompanyManager類並不是它的直接朋友。按照迪米特法則,應該避免類中出現這樣非直接朋友關係的耦合。修改如下

php 設計模式

迪米特法則降低類之間的耦合,讓每個類都減少了不必要的依賴;但是過度使用迪米特法則會產生大量的中介類和傳遞類,導致系統複雜度變大。所以在採用迪米特法則的時候要反覆權衡,既要做到結構清晰,同時做到高內聚低耦合。

開閉原則(Open Close Principle)

一個 軟體實體如類,模組和函式應該對擴充套件開放,對修改關閉。用抽象構建框架,用實現擴充套件細節。當軟體需要變化時,儘量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有的程式碼來實現變化。當我們遵循前面介紹的五大原則,以及使用23種設計模式的目的就是遵循開閉原則。簡單的理解就是構建框架的時候要保持足夠的擴充套件性,通擴充套件來實現修改程式碼而不是直接修改程式碼。

轉自:https://www.cnblogs.com/lina520/p/7993478....

相關文章