设计模式——设计模式之禅day1
單一職責
原則的定義是:應該有且僅有一個原因引起類的變更。
單一職責原則有什么好處:
類的復雜性降低,實現什么職責都有清晰明確的定義;
可讀性提高,復雜性降低,那當然可讀性提高了;
可維護性提高,可讀性提高,那當然更容易維護了;
變更引起的風險降低,變更是必不可少的,如果接口的單一職責做得好,一個接口修改只對相應的實現類有影響,對其他的接口無影響,這對系統的擴展性、維護性都有非常大的幫助。
單一職責原則最難劃分的就是職責。一個職責一個接口,但問題是“職責”沒有一個量化的標準,一個類到底要負責那些職責?這些職責該怎么細化?細化后是否都要有一個接口或類?這些都需要從實際的項目去考慮.
對于接口,我們在設計的時候一定要做到單一,但是對于實現類就需要多方面考慮了。生搬硬套單一職責原則會引起類的劇增,給維護帶來非常多的麻煩,而且過分細分類的職責也會人為地增加系統的復雜性。本來一個類可以實現的行為硬要拆成兩個類,然后再使用聚合或組合的方式耦合在一起,人為制造了系統的復雜性。所以原則是死的,人是活的,這句話很有道理。
單一職責適用于接口、類,同時也適用于方法,什么意思呢?一個方法盡可能做一件事情,比如一個方法修改用戶密碼,不要把這個方法放到“修改用戶信息”方法中。
對于單一職責原則,我的建議是接口一定要做到單一職責,類的設計盡量做到只有一個原因引起變化。
注意: 單一職責原則提出了一個編寫程序的標準,用“職責”或“變化原因”來衡量接口或類設計得是否優良,但是“職責”和“變化原因”都是不可度量的,因項目而異,因環境而異。
里氏替換原則
在面向對象的語言中,繼承是必不可少的、非常優秀的語言機制,它有如下優點:
? 代碼共享,減少創建類的工作量,每個子類都擁有父類的方法和屬性;
? 提高代碼的重用性;
? 子類可以形似父類,但又異于父類,“龍生龍,鳳生鳳,老鼠生來會打洞”是說子擁有父的“種”,“世界上沒有兩片完全相同的葉子”是指明子與父的不同;
? 提高代碼的可擴展性,實現父類的方法就可以“為所欲為”了,君不見很多開源框架的擴展接口都是通過繼承父類來完成的;
? 提高產品或項目的開放性。
自然界的所有事物都是優點和缺點并存的,即使是雞蛋,有時候也能挑出骨頭來,繼承的缺點如下:
? 繼承是侵入性的。只要繼承,就必須擁有父類的所有屬性和方法;
? 降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束;
? 增強了耦合性。當父類的常量、變量和方法被修改時,需要考慮子類的修改,而且在缺乏規范的環境下,這種修改可能帶來非常糟糕的結果—大段的代碼需要重構。
定義:所有引用基類的地方必須能透明地使用其子類的對象。
1. 子類必須完全實現父類的方法
注意:在類中調用其他類時務必要使用父類或接口,如果不能使用父類或接口,則說明類的設計已經違背了LSP原則。
2. 子類可以有自己的個性
3. 覆蓋或實現父類的方法時輸入參數可以被放大
子類在沒有覆寫父類的方法的前提下,子類方法被執行了,這會引起業務邏輯混亂,因為在實際應用中父類一般都是抽象類,子類是實現類,你傳遞一個這樣的實現類就會“歪曲”了父類的意圖,引起一堆意想不到的業務邏輯混亂,所以子類中方法的前置條件必須與超類中被覆寫的方法的前置條件相同或者更寬松。
注意:如果子類不能完整地實現父類的方法,或者父類的某些方法在子類中已經發生“畸變”,則建議斷開父子繼承關系,采用依賴、聚集、組合等關系代替繼承。
4. 覆寫或實現父類的方法時輸出結果可以被縮小
這是什么意思呢,父類的一個方法的返回值是一個類型T,子類的相同方法(重載或覆寫)的返回值為S,那么里氏替換原則就要求S必須小于等于T,也就是說,要么S和T是同一個類型,要么S是T的子類,為什么呢?分兩種情況,如果是覆寫,父類和子類的同名方法的輸入參數是相同的,兩個方法的范圍值S小于等于T,這是覆寫的要求,這才是重中之重,子類覆寫父類的方法,天經地義。如果是重載,則要求方法的輸入參數類型或數量不相同,在里氏替換原則要求下,就是子類的輸入參數寬于或等于父類的輸入參數,也就是說你寫的這個方法是不會被調用的,參考上面講的前置條件。
采用里氏替換原則的目的就是增強程序的健壯性,版本升級時也可以保持非常好的兼容性。即使增加子類,原有的子類還可以繼續運行。在實際項目中,每個子類對應不同的業務含義,使用父類作為參數,傳遞不同的子類完成不同的業務邏輯,非常完美!
依賴倒置原則
包含三層含義:
? 高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;
? 抽象不應該依賴細節;
? 細節應該依賴抽象。
高層模塊和低層模塊容易理解,每一個邏輯的實現都是由原子邏輯組成的,不可分割的原子邏輯就是低層模塊,原子邏輯的再組裝就是高層模塊。那什么是抽象?什么又是細節呢?在Java語言中,抽象就是指接口或抽象類,兩者都是不能直接被實例化的;細節就是實現類,實現接口或繼承抽象類而產生的類就是細節,其特點就是可以直接被實例化,也就是可以加上一個關鍵字new產生一個對象。依賴倒置原則在Java語言中的表現就是:
? 模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類產生的;
? 接口或抽象類不依賴于實現類;
? 實現類依賴接口或抽象類。
更加精簡的定義就是“面向接口編程” —OOD( Object-Oriented Design,面向對象設計)的精髓之一。
采用依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,降低并行開發引起的風險,提高代碼的可讀性和可維護性。
注意:設計是否具備穩定性,只要適當地“松松土”,觀察“設計的藍圖”是否還可以茁壯地成長就可以得出結論,穩定性較高的設計,在周圍環境頻繁變化的時候,依然可以做到“我自巋然不動”。
兩個類之間有依賴關系,只要制定出兩者之間的接口(或抽象類)就可以獨立開發了,而且項目之間的單元測試也可以獨立地運行,而TDD( Test-Driven Development,測試驅動開發)開發模式就是依賴倒置原則的最高級應用。
抽象是對實現的約束,對依賴者而言,也是一種契約,不僅僅約束自己,還同時約束自己與外部的關系,其目的是保證所有的細節不脫離契約的范疇,確保約束雙方按照既定的契約(抽象)共同發展,只要抽象這根基線在,細節就脫離不了這個圈圈,始終讓你的對象做到“言必信,行必果”。
對象的依賴關系有三種方式來傳遞:
1. 構造函數傳遞依賴對象
2. Setter方法傳遞依賴對象
3. 接口聲明依賴對象
在接口的方法中聲明依賴對象, 3.2節的例子就采用了接口聲明依賴的方式,該方法也叫做接口注入。
依賴倒置原則的本質就是通過抽象(接口或抽象類)使各個類或模塊的實現彼此獨立,不互相影響,實現模塊間的松耦合,我們怎么在項目中使用這個規則呢?只要遵循以下的幾個規則就可以:
? 每個類盡量都有接口或抽象類,或者抽象類和接口兩者都具備
這是依賴倒置的基本要求,接口和抽象類都是屬于抽象的,有了抽象才可能依賴倒置。
? 變量的表面類型盡量是接口或者是抽象類
很多書上說變量的類型一定要是接口或者是抽象類,這個有點絕對化了,比如一個工具類,xxxUtils一般是不需要接口或是抽象類的。還有,如果你要使用類的clone方法,就必須使用實現類,這個是JDK提供的一個規范。
? 任何類都不應該從具體類派生
如果一個項目處于開發狀態,確實不應該有從具體類派生出子類的情況,但這也不是絕對的,因為人都是會犯錯誤的,有時設計缺陷是在所難免的,因此只要不超過兩層的繼承都是可以忍受的。特別是負責項目維護的同志,基本上可以不考慮這個規則,為什么?維護工作基本上都是進行擴展開發,修復行為,通過一個繼承關系,覆寫一個方法就可以修正一個很大的Bug,何必去繼承最高的基類呢?(當然這種情況盡量發生在不甚了解父類或者無法獲得父類
代碼的情況下。)
? 盡量不要覆寫基類的方法
如果基類是一個抽象類,而且這個方法已經實現了,子類盡量不要覆寫。類間依賴的是抽象,覆寫了抽象方法,對依賴的穩定性會產生一定的影響。
? 結合里氏替換原則使用
在第2章中我們講解了里氏替換原則,父類出現的地方子類就能出現,再結合本章的講解,我們可以得出這樣一個通俗的規則: 接口負責定義public屬性和方法,并且聲明與其他對象的依賴關系,抽象類負責公共構造部分的實現,實現類準確的實現業務邏輯,同時在適當的時候對父類進行細化。
?依賴倒置原則是6個設計原則中最難以實現的原則,它是實現開閉原則的重要途徑,依賴倒置原則沒有實現,就別想實現對擴展開放,對修改關閉。在項目中,大家只要記住是“面向接口編程”就基本上抓住了依賴倒置原則的核心。
轉載于:https://www.cnblogs.com/gpdm/p/5938633.html
總結
以上是生活随笔為你收集整理的设计模式——设计模式之禅day1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言拼接字符串 -- 使用strcat
- 下一篇: Asp.Net Core--基于角色的授