設計模式大雜燴(24種設計模式的總結及學習設計模式的幾點建議)

程式碼全靠copy發表於2018-04-21

設計模式大雜燴(24種設計模式的總結及學習設計模式的幾點建議)模式分類 & 傳送門 & 對比維度說明

設計原則: 設計模式(總綱)

建立型: 單例模式 簡單工廠模式 工廠方法模式 抽象工廠模式 建造者模式 原型模式

結構型: 代理模式 介面卡模式 裝飾器模式 橋接模式 組合模式 享元模式 外觀模式

行為型: 觀察者模式 模板方法模式 命令模式 狀態模式 職責鏈模式 直譯器模式 中介者模式 訪問者模式 策略模式 備忘錄模式 迭代器模式

以上便是設計模式的分類以及各個模式的傳送門,可以看到其中行為型模式的個數為最多,結構型次之,建立型設計模式最少。

在寫這篇文章的時候,LZ考慮的最多的一個問題就是,從哪幾個維度去對比設計模式能讓大家更加清楚的看出各個設計模式的區別與聯絡,思來想去,LZ決定從以下幾個維度去對比設計模式。

設計原則:描述每個設計模式都遵循了哪些設計原則,破壞了哪些設計原則。

常用場景:描述各個設計模式大部分情況下,都會在哪些場景下出現。

使用概率:主要指在普遍的工作當中,該設計模式出現的頻率,若是類庫或是開源框架提供的功能中包含該模式,則也會計算其頻率。

複雜度:特指一個設計模式在實現的時候的複雜度,主要的衡量標準是類的數量、類之間的耦合關係。

變化點:設計模式很大的一個意義在於容納變化,掌握一個設計模式的變化點是非常重要的一件事。

選擇關鍵點:當選擇使用一個設計模式的時候,指出最關鍵的選擇點在哪裡。

逆鱗:龍有逆鱗,不可觸控,同樣的,設計模式也有逆鱗,有些地方是不能碰的。

相關設計模式:與其它設計模式的關係。

建立型設計模式

單例模式

設計原則:無

常用場景:應用中有物件需要是全域性的且唯一

使用概率:99.99999%

複雜度:低

變化點:無

選擇關鍵點:一個物件在應用中出現多個例項是否會引起邏輯上或者是程式上的錯誤

逆鱗:在以為是單例的情況下,卻產生了多個例項

相關設計模式

原型模式:單例模式是隻有一個例項,原型模式每拷貝一次都會創造一個新的例項。

簡單工廠模式

設計原則:遵循單一職責、違背開閉原則

常用場景:需要在一堆產品中選擇其中一個產品

使用概率:99.99999%

複雜度:低

變化點:產品的種類

選擇關鍵點:一種產品是否可根據某個引數決定它的種類

逆鱗:工廠類不能正常工作

相關設計模式

工廠方法模式:工廠方法模式是簡單工廠模式的進一步抽象化,在這兩者之間做選擇,主要看將工廠進一步抽象化是否有必要,通常情況下,如果工廠的作用僅僅是用來製造產品,則沒必要使用工廠方法模式。

工廠方法模式

設計原則:遵循單一職責、依賴倒置、開閉原則

常用場景:一種場景是希望工廠與產品的種類對客戶端保持透明,給客戶端提供一致的操作,另外一種是不同的工廠和產品可以提供客戶端不同的服務或功能

使用概率:60%

複雜度:中低

變化點:工廠與產品的種類

選擇關鍵點:工廠類和產品類是否是同生同滅的關係

逆鱗:無

相關設計模式

抽象工廠模式:工廠方法模式與抽象工廠模式最大的區別在於,在工廠方法模式中,工廠創造的是一個產品,而在抽象工廠模式中,工廠創造的是一個產品族。

抽象工廠模式

設計原則:遵循單一職責、依賴倒置、開閉原則

常用場景:需要一個介面可以提供一個產品族,且不必知道產品的具體種類

使用概率:30%

複雜度:中

變化點:工廠與產品的種類

選擇關鍵點:產品族是否需要一起提供,且是否有一致的介面

逆鱗:無

相關設計模式

建造者模式:兩者都是建造一批物件或者說產品,不同的是兩者的目的和實現手段,在建造者模式中,是為了複用物件的構建過程而定義了一個指揮者,而在抽象工廠模式中,是為了提供一個這批物件的建立介面而定義了抽象工廠介面。

建造者模式

設計原則:遵循單一職責、開閉原則

常用場景:需要構建一批構建過程相同但表示不同的產品,而構建過程非常複雜

使用概率:10%

複雜度:中

變化點:產品的表示

選擇關鍵點:各個產品的構建過程是否相同

逆鱗:指揮者不能正常工作

原型模式

設計原則:無

常用場景:需要在執行時動態的建立指定例項種類的物件,或是需要複用其狀態

使用概率:10%

複雜度:中低

變化點:無

選擇關鍵點:建立出來的物件是否可以立即投入使用

逆鱗:在以為是深度拷貝的情況下,卻未實現深度拷貝

結構型設計模式

代理模式

設計原則:體現功能複用

常用場景:需要修改或遮蔽某一個或若干個類的部分功能,複用另外一部分功能,可使用靜態代理,若是需要攔截一批類中的某些方法,在方法的前後插入一些一致的操作,假設這些類有一致的介面,可使用JDK的動態代理,否則可使用cglib

使用概率:99.99999%

複雜度:中高

變化點:靜態代理沒有變化點,動態代理的變化點為具有相同切入點的類

選擇關鍵點:靜態代理選擇的關鍵點是是否要複用被代理的部分功能,動態代理選擇的關鍵點在於能否在將被代理的這一批類當中,找出相同的切入點

逆鱗:切入點的不穩定

相關設計模式

介面卡模式:對於介面卡模式當中的定製介面卡,它與靜態代理有著相似的部分,二者都有複用功能的作用,不同的是,靜態代理會修改一部分原有的功能,而介面卡往往是全部複用,而且在複用的同時,介面卡還會將複用的類適配一個介面

介面卡模式

設計原則:遵循開閉原則、體現功能複用

常用場景:需要使用一個類的功能,但是該類的介面不符合使用場合要求的介面,可使用定製介面卡,又或者是有一個介面定義的行為過多,則可以定義一個預設介面卡,讓子類選擇性的覆蓋介面卡的方法

使用概率:40%

複雜度:中

變化點:無

選擇關鍵點:定製介面卡的選擇關鍵點在於是否有更加優良的替代方案,預設介面卡的選擇關鍵點在於介面中的方法是否可以不全部提供,且都有預設方案

逆鱗:無

相關設計模式

裝飾器模式:對於介面卡模式中的定製介面卡與裝飾器模式,二者都是使用組合加繼承的手段,不同的是,介面卡模式的目的在於適配介面,裝飾器模式的目的在於動態的新增功能,且可以疊加。

裝飾器模式

設計原則:遵循迪米特、單一職責、開閉原則,破壞里氏替換,體現功能複用

常用場景:一個類需要動態的新增功能,且這些功能可以相互疊加

使用概率:99.99999%

複雜度:中

變化點:動態新增的功能或者說裝飾器

選擇關鍵點:新增的功能是否需要動態組裝

逆鱗:無

橋接模式

設計原則:遵循單一職責、迪米特、開閉原則,體現功能複用

常用場景:一個物件有多個維度的變化,需要將這些維度抽離出來,讓其獨立變化

使用概率:20%

複雜度:中高

變化點:維度的擴充套件與增加

選擇關鍵點:是否可以將物件拆分成多個不相關的維度

逆鱗:無

組合模式

設計原則:遵循依賴倒置、開閉原則,破壞介面隔離

常用場景:當有一個結構可以組合成樹形結構,且需要向客戶端提供一致的操作介面,使得客戶端操作忽略簡單元素與複雜元素

使用概率:30%

複雜度:中

變化點:節點的數量

選擇關鍵點:對外提供一致操作介面的結構是否可轉化為樹形結構

逆鱗:結構不穩定或結構中的節點有遞迴關係

享元模式

設計原則:無

常用場景:一些狀態相同的物件被大量的重複使用

使用概率:90%

複雜度:中

變化點:無

選擇關鍵點:被共享的物件是否可以將外部狀態提取出來

逆鱗:沒有將外部狀態提取完全

外觀模式

設計原則:遵循迪米特

常用場景:一個子系統需要對外提供服務

使用概率:60%

複雜度:中

變化點:無

選擇關鍵點:子系統對外提供服務是否需要依賴很多的類

逆鱗:子系統對外提供的服務的變化或子系統本身的不穩定

相關設計模式

中介者模式:二者都是為了處理複雜的耦合關係,不同的是外觀模式處理的是類之間複雜的依賴關係,中介者模式處理的是物件之間複雜的互動關係

行為型設計模式

觀察者模式

設計原則:遵循迪米特、開閉原則

常用場景:需要將觀察者與被觀察者解耦或者是觀察者的種類不確定

使用概率:40%

複雜度:中

變化點:觀察者的種類與個數

選擇關鍵點:觀察者與被觀察者是否是多對一的關係

逆鱗:觀察者之間有過多的細節依賴

模板方法模式

設計原則:破壞里氏替換,體現功能複用

常用場景:一批子類的功能有可提取的公共演算法骨架

使用概率:80%

複雜度:中低

變化點:演算法骨架內各個步驟的具體實現

選擇關鍵點:演算法骨架是否牢固

逆鱗:無

命令模式

設計原則:遵循迪米特、開閉原則

常用場景:行為的請求者與行為的處理者耦合度過高

使用概率:20%

複雜度:中高

變化點:命令的種類

選擇關鍵點:請求者是否不需要關心命令的執行只知道接受者

逆鱗:命令的種類無限制增長

相關設計模式

職責鏈模式:容易將二者關聯在一起的原因是,二者都是為了處理請求或者命令而存在的,而且二者都是為了將請求者與響應者解耦,不同的是命令模式中,客戶端需要知道一個命令的接受者,在建立命令的時候就把接受者與命令繫結在一起傳送給呼叫者,而職責鏈模式中,客戶端並不關心最終處理請求的物件是誰,客戶端只是封裝一個請求物件,隨後交給職責鏈的頭部而已,也正因為這樣,二者的實現方式,有著很大的區別

狀態模式

設計原則:遵循單一職責、依賴倒置、開閉原則

常用場景:一個物件在多個狀態下行為不同,且這些狀態可互相轉換

使用概率:20%

複雜度:中

變化點:狀態的種類

選擇關鍵點:這些狀態是否經常在執行時需要在不同的動態之間相互轉換

逆鱗:無

相關設計模式

策略模式:二者的實現方式非常相似,策略介面與狀態介面,具體的策略與具體的狀態以及二者都擁有的上下文,如果看它們的類圖,會發現幾乎一模一樣,而二者不同的地方就在於,狀態模式經常會在處理請求的過程中更改上下文的狀態,而策略模式只是按照不同的演算法處理演算法邏輯,而且從實際場景來講,顧名思義,狀態模式改變的是狀態,策略模式改變的是策略

職責鏈模式

設計原則:遵循迪米特

常用場景:一個請求的處理需要多個物件當中的一個或幾個協作處理

使用概率:15%

複雜度:中

變化點:處理鏈的長度與次序

選擇關鍵點:對於每一次請求是否每個處理的物件都需要一次處理機會

逆鱗:無

直譯器模式

設計原則:遵循單一職責

常用場景:有一種語言被頻繁的使用

使用概率:0.00009%

複雜度:中高

變化點:語言的規則

選擇關鍵點:被頻繁使用的語言是否可用文法表示

逆鱗:語言的規則無限制增長或規則十分不穩定

中介者模式

設計原則:遵循迪米特,破壞單一職責

常用場景:一個系列的物件互動關係十分複雜

使用概率:10%

複雜度:中

變化點:物件之間的互動

選擇關鍵點:複雜的互動關係是否有共性可被中介者承擔

逆鱗:中介者無法工作

訪問者模式

設計原則:遵循傾斜的開閉原則

常用場景:作用於一個資料結構之上的操作經常變化

使用概率:5%

複雜度:高

變化點:資料結構之上的操作

選擇關鍵點:資料結構是否穩定以及操作是否經常變化

逆鱗:資料結構的不穩定

策略模式

設計原則:遵循單一職責、依賴倒置、迪米特、開閉原則

常用場景:演算法或者策略需要經常替換

使用概率:60%

複雜度:中

變化點:策略的種類

選擇關鍵點:客戶端是否依賴於某一個或若干個具體的策略

逆鱗:無

備忘錄模式

設計原則:遵循迪米特、開閉原則

常用場景:需要在物件的外部儲存該物件的內部狀態

使用概率:5%

複雜度:中

變化點:無

選擇關鍵點:是否可以在必要的時候捕捉到物件的內部狀態

逆鱗:大物件的備份

迭代器模式

設計原則:遵循迪米特

常用場景:需要迭代訪問一個聚合物件中的各個元素,且不暴露該聚合物件內部的表示

使用概率:99.99999%

複雜度:中

變化點:聚合物件的種類

選擇關鍵點:客戶端是否關心遍歷的次序

逆鱗:無

相關設計模式

訪問者模式:二者都是迭代的訪問一個聚合物件中的各個元素,不同的是,訪問者模式中,擴充套件開放的部分在作用於物件的操作上,而迭代器模式中,擴充套件開放的部分在聚合物件的種類上,而且二者的實現方式也有著很大的區別。

結束語

以上便是24種設計模式的各個特點與部分模式的對比,如果總結的過程當中有疏漏或是錯誤,請各位不吝賜教,LZ感激不盡。

此外需要說明的是,上面的概率當中有的會出現99.99999%這樣的數字,這是因為這個模式已經嵌入到JAVA類庫或是我們常用的開源框架當中(標註一下:LZ總結的主要針對WEB開發,android的開發LZ並未接觸過,所以不包括在此列),比如迭代器模式,只要你使用過ArrayList或者HashSet,LZ就認為這個模式被使用。這個使用頻率從某種意義上講,可以認為是該模式的重要程度,當然由於這個頻率是LZ制定的,所以僅代表個人觀點。

這裡再針對設計模式的學習,給各位提一點點建議,僅供參考,請各位吸優排差,次序不分先後。

1、之前說過,學習設計模式除了努力之外還要靠緣分,所以如果有設計模式當時怎麼看都不明白,可以暫且放下,之後說不定哪天你突然之間就明白了。(此話並非虛言,LZ很多次的頓悟常發生在上廁所、洗澡、回家路上等一些學習之外的時候。)

2、對於已經在工作的人來說,可以常思考一下,有沒有哪個設計模式可以改善現有的系統架構,但不要輕易付諸實踐。

3、學習設計模式之前,一定要先整明白UML類圖,什麼關聯,依賴,聚合,組合等等都得搞明白兒的,否則學習起來也依然會很吃力。

4、對於初學者,一定要在弄清楚標準的實現程式碼之後,寫一個屬於自己的例子,哪怕是比葫蘆畫瓢,然後仔細體會設計模式使用前後的差異,主要從擴充套件性和類(類包括客戶端,而不僅僅指設計模式中的角色)的職責兩個方面。

5、一定要將設計模式的變化點搞清楚,這點非常重要,甚至重要程度高於設計模式的場景、實現方式以及類和物件之間的耦合關係,很多時候,設計模式的濫用就是因為變化點沒搞清楚,以至於該變化的沒變化,不該變化的經常變化,增加系統的負擔。

6、設計模式不是一次性學習完就可以扔掉不看的東西,而是要經常回過頭來看看,說不定每一次你都有不一樣的體會,而且一般情況下,這些體會會越來越深刻,越來越透徹。

7、如果可能的話,多研究一些開源框架,去找找它們裡面的設計模式。

LZ暫時也就只能想到這些,如果以後有想到再補充吧,各位如果有什麼好的建議也可以與LZ分享一下。

到此為止,整個設計模式系列就真真正正的徹底結束了,當初寫的時候也沒想到自己可以真的堅持下來,雖說整整26篇文章不算多,但是LZ確實花費了大量的時間和精力,值得欣慰的是LZ本人也得到了巨大的收穫,不僅僅是對設計模式的理解日益加深,而且還得了不少猿友的支援,讓LZ對分享這一道路更加堅定。

以後的程式設計之路還很長,對於LZ來說,程式設計並不僅僅是工作,而是一份事業,它給了LZ榮譽、金錢、成就感等等很多東西,希望各位至少在年輕的時候不要被一些悲觀化的觀點所干擾,特別是對程式設計有著熱愛的猿友們,極致才能成就大道,但凡在一個領域有所成就者,大都是鑽研了數十年的成果。

當然,人各有志,LZ無法左右他人,但LZ已經決定了自己的路,學火影裡鳴人的一句話,“這就是我的忍道。”

版權宣告

作者:zuoxiaolong(左瀟龍)

出處:部落格園左瀟龍的技術部落格--http://www.cnblogs.com/zuoxiaolong

您的支援是對博主最大的鼓勵,感謝您的認真閱讀。

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。


相關文章