設計模式學習記錄

  • 2020 年 3 月 16 日
  • 筆記

  最近一段時間學習了設計模式,在此記錄一下自己對於設計模式的理解。

一 設計模式的原則

1、單一職責原則

一個類或者方法只做一件事情,或者說只有一個角色。例如一個簡訊工具類,只負責和簡訊有關的。

(擴展一下,一個類或方法,在寫程式碼的時候每個模組做的事應該是一個水平的,就是說一件事 1,2,3三個步驟,每個步驟有諾干小步驟,小步驟應該放到子方法或子類中,當前模組就負責1,2,3步驟的整合。ps: 程式碼整潔之道裡面的,這裡記錄一下)

2、介面隔離原則

 一個類繼承一個介面,介面的方法都應該是子類中能用的著的 ,不然的話用不著的部分可以  拆分成另外的介面                   

3、依賴倒轉原則

new一個類的時候盡量用這個類的父級去接收,一個系統中,一個類的成員不要是同級別類的子類。

4、里氏替換原則

規範是子類不能改變覆蓋父類的非抽象方法,重載父類方法時,入參可以更寬鬆(一開始沒明白過來,後來反應過來,就是參數使用父類叫重載,是另外一個方法,相當於子類有兩個方法,參數小於等於子類,那就是覆蓋父類方法了,違反歷史轉換原則了)。目的就是在所有父類能用的地方替換成任何子類都能用

5、開閉原則

擴展了功能,對原有的功能不影響

6、迪米特法則

只與直接的朋友發生通訊,直接的朋友指的是成員變數,方法參數,返回類型中的是成員參數,如果方法中出現有類型不是該類所有成員變數,方法參數,返回類型中的任何一個,都違反了迪米特法則

當然,這些原則就類似資料庫三範式,盲目遵守寫出的也並不一定是高效簡潔的程式碼,還是需要掌握一個度才行。

二、設計模式

1、單例模式

單例模式有懶漢式和餓漢式,懶漢指的是需要的時候創建,餓漢式指的是程式運行的時候就創建了。

單例主要有靜態變數、枚舉、雙重鎖和靜態內部類,前兩個都是立即載入,算是餓漢式,後兩者則是執行時載入,算是懶漢式。

2、工廠模式

工廠模式分為簡單工廠、工廠方法以及抽象工廠。

簡單工廠就是一個能夠創建多種對象的工廠模式,問題在於這種方法需要有多個switch或if else分支,不符合開閉原則。

工廠方法是有一個抽象工廠父類,針對每個對象創建不同的工廠實例。(ps:以前一直沒明白,我覺得工廠方法還是需要去if else決定使用什麼工廠,感覺和簡單工廠沒區別,現在明白其實創建實例的地方一般都在寫程式碼的時候明確了需要創建什麼實例,而這種情況簡單工廠還是需要條件分支選擇創建什麼對象,工廠方法則只需要直接使用相應對象的工廠就可以了)。

而抽象工廠和簡單工廠類似,區別在於簡單工廠一般都是只能創建一種對象,而抽象工廠則能夠創建一組對象。

3、原型模式

原型模式差不多就是複製對象的實例,主要需要理解的地方就是淺拷貝和深拷貝的問題。(java深拷貝主要是自定義clone和序列化)

4、建造者模式

建造者模式是在構建一個參數比較多而且構建步驟可以選擇組合時使用較多的一個設計模式。Director這個角色感覺可有可無,這個角色是為了避免調用者去構建過程,不過有時候這個構建過程難免會要由調用者去自定義。

5、適配器模式

適配器宗旨是對原有的類型做一個適配,使得其他不能直接調用的類能夠調用。

這裡可能會疑惑第三方直接調用不行嗎?   可能有一種情況,不能直接改第三方的程式碼,或者想解耦這兩邊的程式碼。          

適配器一般兩種模式,繼承被適配者或者有一個被適配者的成員。

其實看了一些源碼,適配器最常用的方式感覺是這樣的: 載入所有定義好的適配器,不同的調用者在所有適配器中找到自己的適配器,然後執行適配器中的某個方法,該方法對應不同的調用者有不同的實現,達到開閉的原則。

6、橋接模式

如果有兩個維度以上的變化,就可以選擇橋接模式。注意是兩個維度的變化不是兩個變化的地方,兩個維度比如人有男的女的,年齡有老中幼,這種能兩兩組合的才屬於不同維度。

其實我一開始也沒明白啥叫抽象部分與實現部分分離,後來琢磨了一下,可能是我讀錯了,不是與實現部分分離,應該是與實現,部分分離。

也就是說把另外一個維度的變化給分離出去了,還有一點一開始腦子沒轉過來時沒想明白,為啥是一個抽象一個介面,後來仔細看了下程式碼,發現原因是不用抽象類不能用set把介面類給組合進去啊哈哈,當然兩個抽象類也是ok的。

7、裝飾者模式

裝飾者模式主要解決過度的繼承導致程式碼難以維護的問題。首先這個繼承是屬於擴展功能的繼承,子類繼承擴展了父類的功能,比如一個筆的父類,子類是鋼筆毛筆,鉛筆等各個實現完全獨立的裝飾者模式完全也用不上。

為啥能解決呢,看裝飾者模式可以看出子類再多都只需要一個個都繼承一個父類就夠了,再多的子類也是兩層,用繼承的話多少層都有可能,而且最重要的是裝飾者模式可以讓使用者去進行組合達到各種擴展功能,而繼承的話就需要去擴展繼承類了。

裝飾者屬於一種組合,不過這個用於組合的成員都是同一個介面父類。這些個操作感jio和樓上的橋接模式有點類似,像是一個維度的兩兩組合和多個維度的組合

8、組合模式

組合模式是類似於一個葉子節點的結構,場景比較單一。

9、外觀模式

Facade,外觀模式是為了給原本複雜的子系統提供一套簡單的介面,比較類似適配器模式,不過適配器模式更主要是適配,是讓本來不能調用的類變得能夠調用,調用者和被調用者可能都是不可修改的,而外觀模式是為了給客戶端更簡便的調用。

10、享元模式

各種常量池、執行緒池用的就是享元模式,主要就是復用一個對象,免得重複創建浪費性能。享元模式需要注意有內部和外部狀態,內部狀態可以共享,外部狀態會改變。

11、代理模式

代理模式用過java的都知道,沒啥好說的。主要是靜態代理和動態代理,靜態代理就是寫程式碼的時候包裝一下被代理的程式碼,動態代理是運行的時候進行的代理,分為介面代理和cglib方法代理。

12、模板模式

一些什麼生命周期函數,鉤子,用的就是模板方法,大致的流程已經在父類寫好了,不同的操作通過覆蓋幾個特定的方法去實現。

13、命令模式

命令調用者,命令,以及命令接收者構成命令模式,總之就是命令調用者調命令,命令內部調用命令接收者。這個和適配器模式以及外觀模式都有點類似。適配器是適配兩個存在的類,外觀模式是為了簡化介面,而命令模式呢?為啥不直接命令調用者調用接收者?命令模式有一種特定的場景,就是需要記錄日誌回退什麼的,還有就是可以做一個命令隊列,組合命令,這個隊列的動作可以相互組合。還有存粹為了命令調用者和命令接收者的解耦。而且試想一下,命令模式就像遙控器,我現在有一個遙控器,遙控器的按鍵是固定的,這時候我可以替換按鍵的命令去執行不同的操作。

 

14、訪問者模式

訪問者模式為了解決一個數據結構類中對於對象操作新增方法的需求,因為新增方法就需要在數據結構類中新增程式碼,不符合開閉原則,訪問者模式把新增方法放到訪問者類中,可以轉換為新增不同的訪問者類。訪問者模式還可以有一個特點,就是訪問者visitor的對不同對象的訪問方法都是一個重載方法,參數是父類的不同實現類,當然這是違反了依賴倒置的。所以這種訪問者的一大好處就是不用通過一推ifelse去判斷,而是通過類型自動推斷執行對應的方法,這個比較適合的場景就是在一個遍歷中。訪問者主要是擴充了原來類的方法,或者說將原本類中沒啥關係的行為抽取出來。

15、迭代器模式

迭代器模式同樣是java中比較常見的一個模式,不來細說了。

16、觀察者模式

初步覺得觀察者模式的應用場景比較單一,主要就是一種發布訂閱的模式,在java中已經有實現好的觀察者模式的類 Observable。

17、中介模式

中介者模式感覺就像專門為了實現迪米特法則的一種模式。是為了解決各種類之間複雜的調用關係。和外觀模式類似,區別在於中介模式主要為了解決類之間的相互調用,外觀模式是給多個複雜的類統一對外提供介面。

18、備忘錄模式

用來記錄對象的狀態,可以用來回退到其中一個狀態

19、解釋器模式

解析語法或者是表達式時能用到的一種設計模式

20、狀態模式

狀態在對象內部,由狀態來決定行為

21、策略模式

策略模式適用於一些行為比較相近的對象,通過父類的成員變數,可以子類相互轉換

22、職責鏈模式

java就直接參考springmvc的過濾器鏈

 

 

以上都是我在學習設計模式後的個人觀點,有些地方也是一知半解的,希望看到部落格的能多多討論一下!