二十三種設計模式修煉手冊
不知不覺,在開發這條道路上摸爬打滾也有些年頭了,偶爾回頭看看以前寫的程式碼,真可謂粗糙至極。當然了,那時候還是小白,程式碼寫得難看些情有可原,不過現在可不能再用以前的標準去衡量自己了,因此掌握一些高階架構技巧是必須的,設計模式正是一個很好的敲門磚。
在我看來,設計模式不僅僅只是一套模板,要想掌握設計模式並做到舉一反三,必須深入理解其中的思想,這個模式是為了解決什麼問題?解決的思路是什麼?程式碼的實現又如何?如果問題細節發生了微小的變化又該如何處理?所以說思考很重要,不能死記硬背,一定要多想。
寫下這篇文章,是為了梳理自己的知識點,做個記錄。如果有來人看到了,並且對你有幫助的話,我也會很開心,因為知識是要傳播的,大家都樂於分享自己的見解,才能共同進步。
1|0設計模式的定義
模式一詞起源於建築業,描述瞭解決問題的核心方法。通過這種方式,可以多次重用那些已有的解決方案,無須重複相同的工作。
模式可以應用於不同的領域,軟體模式是將模式的一般概念應用於軟體開發領域,可以被認為是對軟體開發中某一特定問題的解法的某種統一表示。軟體模式並非僅限於設計模式,還包括架構模式、分析模式和過程模式等,在軟體生存期的每一個階段都存在著一些被認同的模式。
在軟體模式領域,目前研究最深入的是設計模式。設計模式是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結,使用這些設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。
2 |0設計模式的基本要素
設計模式一般有如下幾個基本要素:模式名稱、問題、目的、解決方案、效果、例項程式碼和其他相關設計模式,其中的關鍵元素包括以下四個方面:
模式名稱:通過一兩個詞來描述模式的問題、解決方案和效果,以更好地理解模式並方便開發人員之間的交流。
問題:描述了應該在何時使用模式,它包含了設計中存在的問題以及問題存在的原因。有時候問題描述可能會包含使用該模式時必須滿足的一系列先決條件。
解決方案:描述了設計模式的組成部分,以及這些組成部分之間的相互關係,各自職責和協作方式。解決方案並不描述一個特定而具體的設計或實現,而是提供設計問題的抽象描述和怎樣用一個具有一般意義的元素組合(類或者物件)來解決這個問題。
效果:描述模式應有的效果以及在使用模式時應權衡的問題。效果主要包含模式的優缺點分析,因此需要綜合考慮模式的效果。
3|0設計模式的分類
設計模式一般有兩種分類方式,一種是根據目的分類(模式是用來做什麼的),另一種則是根據範圍分類(模式是用來處理類之間的關係還是處理物件之間的關係)。根據這兩種分類分別有如下兩張表供參考:
範圍\目的 | 建立型模式 | 結構型模式 | 行為型模式 |
---|---|---|---|
類模式 | 工廠方法模式 | (類)介面卡模式 | 直譯器模式 模板方法模式 |
物件模式 | 抽象工廠模式 建造者模式 原型模式 單例模式 |
(物件)介面卡模式 橋接模式 組合模式 裝飾模式 外觀模式 享元模式 代理模式 |
職責鏈模式 命令模式 迭代器模式 中介者模式 備忘錄模式 觀察者模式 狀態模式 策略模式 訪問者模式 |
下面簡單對二十三種設計模式進行說明
模式類別 | 模式名稱 | 模式說明 |
---|---|---|
建立型模式 | ||
抽象工廠模式 | 提供了一個建立一系列相關或相互依賴物件的介面,而無須指定它們具體的類 | |
工廠方法模式 | 該類的例項化操作延遲到子類中完成,即由子類來決定究竟該例項化(建立)哪一個類 | |
建造者模式 | 將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示 | |
原型模式 | 通過給出一個原型物件來指明要建立物件的型別,然後通過複製這個原型物件來建立更多同類型的物件 | |
單例模式 | 確保在系統中某一個類只有一個例項,可以自行例項化並向整個系統提供這個例項 | |
結構型模式 | ||
介面卡模式 | 將一個介面轉換成客戶希望的另一個介面,從而使介面不相容的那些類可以一起工作 | |
橋接模式 | 將抽象部分與它的實現部分分離,使它們都可以獨立地變化 | |
組合模式 | 組合多個物件形成樹形結構以表示“整體-部分”的結構層次 | |
裝飾模式 | 動態地給一個物件增加一些額外的職責 | |
外觀模式 | 為複雜子系統提供一個一致的介面 | |
享元模式 | 通過運用共享技術有效地支援大量細粒度物件的複用 | |
代理模式 | 給某一個物件提供一個引用,並由代理物件控制對原物件的引用 | |
結構型模式 | ||
職責鏈模式 | 避免請求傳送者與接收者耦合在一起,讓多個物件都有可能接收請求,將這些物件連線成一條鏈,並且沿著這條鏈傳遞請求,直到有物件處理為止 | |
職責鏈模式 | 避免請求傳送者與接收者耦合在一起,讓多個物件都有可能接收請求,將這些物件連線成一條鏈,並且沿著這條鏈傳遞請求,直到有物件處理為止 | |
命令模式 | 將一個請求封裝為一個物件,從而使得請求呼叫者和請求接收者解耦 | |
直譯器模式 | 描述如何為語言定義一個語法,如何在該語言中表示一個句子,以及如何解釋這些句子 | |
迭代器模式 | 提供了一種方法來訪問聚合物件,而不用暴露這個物件的內部表示 | |
中介者模式 | 通過一箇中介物件來封裝一系列的物件互動,使得各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動 | |
備忘錄模式 | 在不破壞封裝的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣可以在以後將物件恢復到原先儲存的狀態 | |
觀察者模式 | 定義了物件間的一種一對多依賴關係,使得當每一個物件狀態發生改變時,其相關依賴物件皆得到通知並被自動更新 | |
狀態模式 | 允許將一個物件在其內部狀態改變時改變它的行為 | |
策略模式 | 定義一系列演算法,並將每一個演算法封裝在一個類中,讓它們可以相互替換,策略模式讓演算法獨立於使用它的客戶而變化 | |
模板方法模式 | 定義一個操作中演算法的骨架,而將一些步驟延遲到子類中 | |
訪問者模式 | 表示將一個作用於某物件結構中的各元素操作,它使得使用者可以在不改變各元素的類的前提下定義作用於這些元素的新操作 |
以上就是設計模式的簡單介紹,下面是一些補充知識,如果已經掌握了可以不看。這些知識雖然不屬於設計模式範疇,但對於我們理解設計模式有莫大的好處。
4|0統一建模語言
統一建模語言(UML)是一種視覺化的標準建模語言,通過UML可以構造軟體系統的藍圖。在設計模式中,使用UML來分析和設計每一個模式的結構,描述每一個模式例項,幫助我們深入理解設計模式。
比如要蓋一棟房子,需要先設計圖紙,設計圖紙就是一種設計語言,也就是模型語言。在一個現代化工程中,人們要溝通和協作,就必須使用標準的工業化設計語言,通過建模進行描述,把所要設計的結構和系統的行為聯絡起來,對系統的結構進行視覺化控制。
5|0UML 結構
UML 是由圖形符號表達的建模語言,其主要包括以下幾個部分:
檢視
使用不同的檢視從不同角度來描述軟體系統,包括:
使用者檢視:以使用者觀點表示系統的目標,它是所有檢視的核心,該檢視描述系統的需求
結構檢視:表示系統的靜態行為和靜態元素,如包、類與物件,以及它們之間的關係
行為檢視:表示系統的動態行為,描述組成元素如物件在系統執行時的互動關係
實現檢視:表示系統中邏輯元素的分佈,描述系統中物理檔案以及它們之間的關係
環境檢視:表示系統中物理元素的分佈,描述系統中硬體裝置以及它們之間的關係
圖
提供了十三種與上述五種檢視相對。在設計模式的學習中,重點關注類圖、順序圖和狀態圖即可。
用例圖:對應使用者檢視。在用例圖中,使用用例來表示系統的功能需求,用例圖表示多個外部執行者與系統用例之間以及用例與用例之間的關係。
類圖:對應於結構檢視。類圖使用類來描述系統的靜態結構,類圖包括類和它們之間的關係。
物件圖:對應於結構檢視。物件圖用於表示類的物件例項之間的關係。
包圖:對應於結構檢視。描述包與包之間的關係。
組合結構圖:對應於結構檢視。表示一個類的內部結構。
狀態圖:對應於行為檢視。描述一系列物件的狀態及狀態之間的轉換。
活動圖:對應於行為檢視。表示系統中各種活動的次序。
順序圖:又稱時序圖或序列圖,對應於行為檢視。表示對互動,重點表示物件之間傳送訊息的時間順序。
定時圖:對應於行為檢視。定時圖採用一種帶數字刻度的時間軸來描述訊息的順序,相比順序圖來說更加精確。
互動概覽圖:對應於行為檢視。可以把互動概覽圖理解為細化的活動圖,在其中的活動都通過一些小型的順序圖來表示。
元件圖:又稱構件圖,對應於實現檢視。描述每個功能所在元件位置以及它們之間的位置。
部署圖:又稱實施圖,對應於環境檢視。描述軟體中各個元件駐留的硬體位置以及這些硬體之間的互動關係。
模型元素
模型元素包括事物以及事物之間的聯絡。事物代表任何可以定義的東西,事物之間的關係把事物聯絡在一起,組成有意義的結構模式
通訊機制
為模型元素提供額外的註釋、修飾和語義。
6|0面向物件設計原則
面向物件設計原則是學習設計模式的基礎,每一種設計模式都符合某一種或多種面向物件設計原則。在軟體開發中使用這些原則可以提高軟體的可維護性和可複用性,讓我們可以設計出更加靈活也更容易擴充套件的軟體設計,實現可維護性複用的目標。
單一職責原則
一個物件應該只包含單一的職責,並且該職責被完整地封裝在一個類中
一個類承擔的職責越多,被複用的可能性越小,並且相當於將這些職責耦合在一起。因此需要將這些職責進行分離,實現高內聚、低耦合的指導方針。
開閉原則
一個軟體實體應當對擴充套件開放,對修改關閉。也就是說在設計一個模組,應當使這個模組可以在不被修改的前提下被擴充套件。
在開閉原則的定義中,軟體實體可以是一個軟體模組、一個由多個類組成的區域性結構或一個類。
軟體的需求會隨著時間推移發生變化,如果軟體設計符合開閉原則,就可以在擴充套件時無須修改現有程式碼,保證穩定性與延續性。
抽象化是滿足開閉原則的關鍵,通過定義一個相對穩定的抽象層,將不同的實現行為在具體實現層中實現。如果需要修改,無須改動抽象層,只需增加新的實體類來實現新的業務功能即可。
里氏代換原則
所有引用基類(父類)的地方必須能透明地使用其子類的物件。說白了就是:在軟體中如果能使用其基類物件,那麼一定能使用其子類物件。把基類都替換成它的子類,程式不會產生任何錯誤。但反過來則不成立,如果一個軟體實體使用的是一個子類,那麼它不一定能使用基類。
里氏代換原則是實現開閉原則的重要方式之一,在程式中儘量使用基類型別來定義物件,而在執行時再確定其子類型別,用子類物件來替代父類物件。
依賴倒轉原則
高層模組不應該依賴低層模組,它們都應該依賴抽象。抽象不應該依賴於細節,細節應該依賴於抽象。即程式碼要依賴於抽象的類,而不依賴於具體的類,要針對介面程式設計,不要針對實現程式設計。
如果說開閉原則是面向物件設計的目標的話,那麼依賴倒轉原則就是面向物件設計的主要手段。下面介紹依賴倒轉原則中經常提到的兩個概念。
類之間的耦合:在面向物件系統中,兩個類之間通常可以發生三種不同的耦合關係(依賴關係)
1. 零耦合關係:兩個類之間沒有任何耦合關係 2. 具體耦合關係:兩個具體類之間存在一個類對另一個具體類例項的直接引用 3. 抽象耦合關係:發生在一個具體類和抽象類之間,也可以發生在兩個抽象類之間。依賴倒轉原則要求客戶端依賴於抽象耦合。
依賴注入:簡單來說,依賴注入就是將一個類的物件傳入另一個類,注入時應該注入父類物件,而在程式執行時再通過子類物件來覆蓋父類物件。依賴注入有三種方式
1. 構造注入:通過建構函式注入例項變數 2. 設值注入:通過Setter方法注入例項變數 3. 介面注入:通過介面方法注入例項變數
介面隔離原則
一旦一個介面太大,則需要將它分割成一些更小的介面,使用該介面的客戶端僅需知道與之相關的方法即可。
這裡的介面往往有兩種不同的含義:
一種是指一個型別所具有的方法的特徵的集合,僅僅是邏輯上的概念,介面的劃分將直接帶來型別的劃分。此時可以把介面理解成角色,一個介面只代表一個角色,每個角色都有它特有的一個介面,此時這個原則叫做角色隔離原則。
另一種是指介面僅僅提高客戶端需要的行為,即所需的方法。介面應該儘量細化,介面中的方法進來少,每個介面只包含一個客戶端所需的角色。
合成複用原則
儘量使用組合物件,而不是繼承來達到複用的目的。通俗來說,合成複用原則就是指一個新的物件裡通過關聯關係(包括組合關係和聚合關係)來使用一些已有物件,使之成為新物件的一部分;新物件通過委派呼叫已有物件的方法達到複用已有功能的目的。
通過繼承來實現複用很簡單,子類可以覆蓋父類方法,易於擴充套件。但會破壞系統的封裝性,因為繼承會將基類的實現細節暴露給子類,這種複用又稱為“白箱複用”。
通過組合/聚合來複用是將一個類的物件作為另一個類的物件的一部分。新物件可以呼叫已有物件的功能,這種複用又稱為“黑箱複用”。
迪米特法則
指一個軟體實體應儘可能少的與其他實體發生相互作用。當一個模組修改時,就會盡量少的影響其他模組,這是對軟體實體之間通訊的限制,它要求軟體實體之間通訊的寬度和深度。
在迪米特法則中,對於一個物件,其朋友包括以下幾類:
當前物件本身(this)
以引數形式傳入到當前物件方法中的物件
當前物件的成員物件
如果當前物件的成員物件是一個集合,那麼集合中的元素也都是朋友
當前物件所建立的物件
任何物件如果滿足上面的條件之一,就是當前物件的“朋友”,否則就是“陌生人”。
狹義的迪米特法則:如果兩個類之間不必彼此通訊,那麼這兩個類就不應當發生直接的相互作用。如果其中一個類需要呼叫另一個類的某一個方法的話,可以通過第三者轉發這個呼叫。狹義的迪米特法則可以降低類之間的耦合,但也會造成系統不同模組之間通訊效率降低,使得系統的不同模組之間不容易協調。
廣義的迪米特法則:指物件之間的資訊流量、流向以及資訊的影響的控制,主要是對資訊隱藏的控制。資訊的隱藏可以使各個子系統之間脫耦,每一個模組不依賴於其他模組存在,因此每一個模組都可以獨立地在其他地方使用。
__EOF__
本文連結:https://www.cnblogs.com/Yee-Q/p/13326782.html