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