
GOF23種設(shè)計模式中結(jié)構(gòu)型模式,共七種:
- 適配器模式、
- 裝飾器模式、
- 代理模式、
- 外觀模式、
- 橋接模式、
- 組合模式、
- 享元模式。
下面逐一講解:
其他同系列的文章還有:
面向?qū)ο缶幊讨械牧笤瓌t
設(shè)計模式| 創(chuàng)建型模式
設(shè)計模式| 結(jié)構(gòu)型模式
設(shè)計模式| 行為型模式 (上)
設(shè)計模式| 行為型模式 (下)
歡迎閱讀,評論!??!
1.適配器模式
不兼容結(jié)構(gòu)的協(xié)調(diào)——適配器模式
適配器模式的作用是解決兩個對象間的接口不兼容的問題。使用適配器模式之后,原本由于接口不兼容而不能工作的兩個對象可以一起工作。
舉個生活中的例子:港式插頭轉(zhuǎn)換器,港式的電器插頭比大陸的電器插頭體積要大一些。如果從香港買了一個 Mac book,
我們會發(fā)現(xiàn)充電器無法插在家里的插座上,為此而改造家里的插座顯然不方便,
所以我們需要一個轉(zhuǎn)換插頭。
相信在開發(fā)中遇到過這樣的情況:原本兩個類是毫無聯(lián)系的,但是有些時候我們想讓他們進行交互協(xié)作,
當然最直接暴力的就是修改各自的源碼,但沒有源碼呢,
通常適配器模式所涉及的角色包括三種:
目標(Target)——客戶端期望使用的接口或類(新接口)
被適配者(Adaptee)——一個現(xiàn)存需要適配的接口。(舊接口)
適配器(Adapter)——負責(zé)將Adaptee的接口轉(zhuǎn)換為Target的接口。適配器是一個具體的類,這是該模式的核心。
1、適配器模式的使用
A. 適配器必須實現(xiàn)原有的舊的接口(現(xiàn)有的可利用的接口)。
B. 適配器對象中持有對新接口的引用,當調(diào)用舊接口時,將這個調(diào)用委托給實現(xiàn)新接口的對象來處理,也就是在適配器對象中組合一個新接口(組合的形式)。
這就是適配器模式的魅力:不改變原有接口,卻還能使用新接口的功能。
2、適配器模式的優(yōu)點
* 通過適配器模式可以讓兩個原本沒有任何關(guān)系的類能夠在一起協(xié)調(diào)運行,而無需改動原來的代碼,極大的增加了可擴展性。
* 將目標類和適配者類解耦
* 增加了類的透明性和復(fù)用性,將具體的實現(xiàn)封裝在適配者類中,對于客戶端類來說是透明的,而且提高了適配者的復(fù)用性,符合開閉原則
3、適配器模式的缺點
過多的使用適配器,可能會導(dǎo)致系統(tǒng)邏輯非常零亂,不易整體進行把握,降低代碼的可讀性,
并增加了一些冗余的代碼,如果可以對系統(tǒng)進行重構(gòu),盡量不要使用使用適配器模式。
如果一定要與外觀、裝飾者模式嚴格區(qū)分的話,
裝飾者模式主要是添加新的功能,而適配器模式主要做的是轉(zhuǎn)換工作。
適配器將一個對象包裝起來以改變其接口,
裝飾者將一個對象包裝起來以增加新的行為和責(zé)任,
適配器模式的核心在于“轉(zhuǎn)換”——盡量通過適配器把現(xiàn)有資源轉(zhuǎn)為可用的目標資源。
2.裝飾器模式
擴展系統(tǒng)功能——裝飾器模式
無論是在現(xiàn)實生活還是編程世界中,除了本質(zhì)還需要一些“外飾”才能達到最好的效果。
尤其是在項目開發(fā)過程中,很多業(yè)務(wù)都充滿了很多的不確定性,來自業(yè)務(wù)本身、產(chǎn)品、項目特性的,
如果一味的使用繼承來擴展的話,可能會相當不靈活,今天介紹的裝飾器模式就是多層繼承的替代方案。
又例如,某一天小王去度假了,但是他寫的函數(shù)需要增加一些功能,此時由我接手,
但是我并不想修改他的源代碼,此時可以使用裝飾者模式。裝飾者模式是能夠在不改變對象自身的基礎(chǔ)上,
在程序運行期間給對象動態(tài)地添加功能。
裝飾模式(Decorator Pattern)是一種常見的結(jié)構(gòu)型模式,又叫包裝模式(Wrapper),又叫包裝器模式(Wrapper),它可以動態(tài)地給一個對象添加一些額外的職責(zé)功能。
裝飾模式是對客戶端以透明的方式擴展對象的功能,是繼承關(guān)系的一個替代方案。
即對于客戶端來說并不會覺得對象在裝飾前后有何不同,而且裝飾模式可以在不用創(chuàng)造更多子類的情況下將對象的功能加以擴展,
更關(guān)鍵在于這種擴展是完全透明的。通常裝飾器模式中會涉及到兩大類角色——被裝飾對象和裝飾者,
又可以細分為四種具體角色:
Component抽象構(gòu)件——定義我們最核心的對象的一個接口或者是抽象類,
即最原始的對象(必然有一個最基本、最核心、最原始的接口或抽象類充當Component抽象構(gòu)件)
ConcreteComponent 具體構(gòu)件——定義一個要被裝飾器裝飾的對象,Component 的具體實現(xiàn),即被裝飾者。
抽象Decorator裝飾角色 ——一般是一個抽象類(便于根據(jù)不同的裝飾邏輯去實現(xiàn)其子類)且一定會持有一個private變量指向
Component抽象構(gòu)件的引用(它里面不一定只有抽象的方法)維護對組件對象和其子類組件的引用,
但是要注意的是所謂裝飾者僅僅是發(fā)揮錦上添花的作用,核心的本質(zhì)功能還是應(yīng)該由構(gòu)件提供,
相當于是把在構(gòu)件的基礎(chǔ)上進行升級,所以需要繼承構(gòu)件
具體裝飾器角色(ConcreteDecorator)——通常是一群子裝飾器組合共同為組件添加新的功能
抽象裝飾器核心代碼
#import "Component.h" //抽象構(gòu)件
@interface Decorator : Component
//裝飾對象需要裝飾的原始對象
@property(nonatomic, strong)Component *component;
@end
-(void)operation{
if (self.component) {
[self.component operation];
}
}
1、裝飾器模式的優(yōu)點
* 雖然裝飾模式與繼承關(guān)系的都是為了要在基類的基礎(chǔ)上擴展對象的功能,
但是裝飾模式可以提供比繼承更多的靈活性,裝飾模式允許系統(tǒng)動態(tài)決定“貼上”或者除掉一個“裝飾”而不需要改變代碼的層次;
而繼承關(guān)系是靜態(tài)的,如果想要增加或者減少一個功能只能通過增加繼承層次或降低層次。
* 使用者可以隨時根據(jù)具體的業(yè)務(wù)邏輯靈活組織所需要的功能,通過使用不同的具體裝飾類以及這些裝飾類的組合即可
* 裝飾者類可以在被裝飾者的行為前面或后面加上自己的行為,甚至取代被裝飾者的行為
* 通過使用裝飾器模式,我們可以實現(xiàn)不改變原有代碼,開放現(xiàn)有代碼的方式來實現(xiàn)更多的功能。
* 裝飾類和被裝飾類是可以獨立發(fā)展且不會相互耦合。即Component類無須知道Decorator類,
Decorator類是從外部來擴展Component類的功能,而Decorator也不用知道具體的構(gòu)件。
2、裝飾器模式的缺點
使用裝飾模式會產(chǎn)生比使用繼承關(guān)系更多的對象和復(fù)雜的邏輯,降低了可讀性,
因此,合理使用裝飾類,控制其數(shù)量,以便降低系統(tǒng)的復(fù)雜度
3、裝飾器模式的適用場景及注意事項
在不影響其他對象的情況下,需要以動態(tài)、透明的方式給隨時給被裝飾對象添加或者移除某些功能時而不影響原有邏輯,
想通過靈活組合對象的方式動態(tài)地改變被裝飾對象的行為
需要擴展一個類的功能或給一個類增加或隨時移除附加功能。
當需要以多層次的繼承關(guān)系才能實現(xiàn)擴充時,可以考慮裝飾器模式。
需要為某一類型的兄弟類進行改裝或加裝功能,首選裝飾模式。
3.代理模式
代理模式(Proxy Pattern)又叫委托模式屬于是一個使用率非常高結(jié)構(gòu)型設(shè)計模式。
其定義如下:為其他對象提供一種代理以控制對這個對象的訪問。(Provide a surrogate or placeholder for another object to control access to it.)通俗可以理解成代理就是幫你打理。你只跟代理一個人打交到。而不關(guān)心實際操作的人的具體如何做。代理模式目前框架里用的最多,
主要作用是程序本身不關(guān)心被代理的身份細節(jié)。而只關(guān)心它暴露出來的共有行為接口,也可從字面理解就是代理你去執(zhí)行調(diào)用別的類的方法面向被調(diào)用的類。
1、代理模式的優(yōu)點
能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,在一定程度上降低了系統(tǒng)的耦合度。
客戶端可以針對抽象主題角色進行編程,增加和更換代理類無須修改源代碼,符合開閉原則,
系統(tǒng)具有較好的靈活性和可擴展性。
2、適用場景
當無法或者不想直接訪問某個對象或訪問直接某個對象消耗巨大時,可以采取通過一個代理對象來間接訪問,
為了保持對客戶端透明,代理對象和被代理對象需要實現(xiàn)相同的接口。
4.外觀模式
外觀模式:為子系統(tǒng)中的一組接口提供一個統(tǒng)一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。
外觀模式中,一個子系統(tǒng)的外部與其內(nèi)部的通信通過一個統(tǒng)一的外觀類進行,外觀類將客戶類與子系統(tǒng)的內(nèi)部復(fù)雜性分隔開,使得客戶類只需要與外觀角色打交道,而不需要與子系統(tǒng)內(nèi)部的很多對象打交道。
外觀模式是一種使用頻率非常高的結(jié)構(gòu)型設(shè)計模式,它通過引入一個外觀角色來簡化客戶端與子系統(tǒng)之間的交互,
為復(fù)雜的子系統(tǒng)調(diào)用提供一個統(tǒng)一的入口,降低子系統(tǒng)與客戶端的耦合度,且客戶端調(diào)用非常方便。
外觀模式又稱為門面模式,它是一種對象結(jié)構(gòu)型模式。外觀模式是迪米特法則的一種具體實現(xiàn),
通過引入一個新的外觀角色可以降低原有系統(tǒng)的復(fù)雜度,同時降低客戶類與子系統(tǒng)的耦合度。
外觀模式概述
不知道大家有沒有比較過自己泡茶和去茶館喝茶的區(qū)別,如果是自己泡茶需要自行準備茶葉、茶具和開水,如圖1(A)所示,
而去茶館喝茶,最簡單的方式就是跟茶館服務(wù)員說想要一杯什么樣的茶,是鐵觀音、碧螺春還是西湖龍井?正因為茶館有服務(wù)員,
顧客無須直接和茶葉、茶具、開水等交互,整個泡茶過程由服務(wù)員來完成,顧客只需與服務(wù)員交互即可,整個過程非常簡單省事,
如圖1(B)所示。

在軟件開發(fā)中,有時候為了完成一項較為復(fù)雜的功能,一個客戶類需要和多個業(yè)務(wù)類交互,而這些需要交互的業(yè)務(wù)類經(jīng)常會作為一個整體出
現(xiàn),由于涉及到的類比較多,導(dǎo)致使用時代碼較為復(fù)雜,此時,特別需要一個類似服務(wù)員一樣的角色,
由它來負責(zé)和多個業(yè)務(wù)類進行交互,而客戶類只需與該類交互。
外觀模式通過引入一個新的外觀類(Facade)來實現(xiàn)該功能,外觀類充當了軟件系統(tǒng)中的“服務(wù)員”,
它為多個業(yè)務(wù)類的調(diào)用提供了一個統(tǒng)一的入口,簡化了類與類之間的交互。
在外觀模式中,那些需要交互的業(yè)務(wù)類被稱為子系統(tǒng)(Subsystem)。如果沒有外觀類,
那么每個客戶類需要和多個子系統(tǒng)之間進行復(fù)雜的交互,系統(tǒng)的耦合度將很大,如圖2(A)所示;
而引入外觀類之后,客戶類只需要直接與外觀類交互,客戶類與子系統(tǒng)之間原有的復(fù)雜引用關(guān)系由外觀類來實現(xiàn),從而降低了系統(tǒng)的耦合度
如圖2(B)所示。

5.橋接模式
處理多維度變化-橋接模式
在正式介紹橋接模式之前,我先跟大家談?wù)剝煞N常見文具的區(qū)別,它們是毛筆和蠟筆。
假如我們需要大中小3種型號的畫筆,能夠繪制12種不同的顏色,
如果使用蠟筆,需要準備3×12 = 36支,
但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及到的對象個數(shù)僅為 3 + 12 = 15,遠小于36,
卻能實現(xiàn)與36支蠟筆同樣的功能。
如果增加一種新型號的畫筆,并且也需要具有12種顏色,對應(yīng)的蠟筆需增加12支,而毛筆只需增加一支。為什么會這樣呢?
通過分析我們可以得知:在蠟筆中,顏色和型號兩個不同的變化維度(即兩個不同的變化原因)融合在一起,
無論是對顏色進行擴展還是對型號進行擴展都勢必會影響另一個維度;但在毛筆中,顏色和型號實現(xiàn)了分離,
增加新的顏色或者型號對另一方都沒有任何影響。如果使用軟件工程中的術(shù)語,我們可以認為在蠟筆中顏色和型號之間存在較強的耦合性,
而毛筆很好地將二者解耦,使用起來非常靈活,擴展也更為方便。
在軟件開發(fā)中,我們也提供了一種設(shè)計模式來處理與畫筆類似的具有多變化維度的情況,即本章將要介紹的橋接模式。
橋接模式是一種很實用的結(jié)構(gòu)型設(shè)計模式,如果軟件系統(tǒng)中某個類存在兩個獨立變化的維度,通過該模式可以將這兩個維度分離出來,使兩者可以獨立擴展,讓系統(tǒng)更加符合“單一職責(zé)原則”。與多層繼承方案不同,它將兩個獨立變化的維度設(shè)計為兩個獨立的繼承等級結(jié)構(gòu),并且在抽象層建立一個抽象關(guān)聯(lián),該關(guān)聯(lián)關(guān)系類似一條連接兩個獨立繼承結(jié)構(gòu)的橋,故名橋接模式。
橋接模式用一種巧妙的方式處理多層繼承存在的問題,
用抽象關(guān)聯(lián)取代了傳統(tǒng)的多層繼承,將類之間的靜態(tài)繼承關(guān)系轉(zhuǎn)換為動態(tài)的對象組合關(guān)系,
使得系統(tǒng)更加靈活,并易于擴展,同時有效控制了系統(tǒng)中類的個數(shù)。
橋接定義如下:
橋接模式(Bridge Pattern):將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化。它是一種對象結(jié)構(gòu)型模式,
又稱為柄體(Handle and Body)模式或接口(Interface)模式。
橋接模式的結(jié)構(gòu)與其名稱一樣,存在一條連接兩個繼承等級結(jié)構(gòu)的橋。
橋接模式是一個非常有用的模式,在橋接模式中體現(xiàn)了很多面向?qū)ο笤O(shè)計原則的思想,包括“單一職責(zé)原則”、“開閉原則”、“合成復(fù)用原則”、“里氏代換原則”、“依賴倒轉(zhuǎn)原則”等。熟悉橋接模式有助于我們深入理解這些設(shè)計原則,也有助于我們形成正確的設(shè)計思想和培養(yǎng)良好的設(shè)計風(fēng)格。
在使用橋接模式時,我們首先應(yīng)該識別出一個類所具有的兩個獨立變化的維度,
將它們設(shè)計為兩個獨立的繼承等級結(jié)構(gòu),為兩個維度都提供抽象層,并建立抽象耦合。
通常情況下,我們將具有兩個獨立變化維度的類的一些普通業(yè)務(wù)方法和與之關(guān)系最密切的維度設(shè)計為“抽象類”層次結(jié)構(gòu)(抽象部分),
而將另一個維度設(shè)計為“實現(xiàn)類”層次結(jié)構(gòu)(實現(xiàn)部分)。
例如:對于毛筆而言,由于型號是其固有的維度,因此可以設(shè)計一個抽象的毛筆類,
在該類中聲明并部分實現(xiàn)毛筆的業(yè)務(wù)方法,而將各種型號的毛筆作為其子類;
顏色是毛筆的另一個維度,由于它與毛筆之間存在一種“設(shè)置”的關(guān)系,
因此我們可以提供一個抽象的顏色接口,而將具體的顏色作為實現(xiàn)該接口的子類。
在此,型號可認為是毛筆的抽象部分,而顏色是毛筆的實現(xiàn)部分,
結(jié)構(gòu)示意圖如下:

在上圖中,如果需要增加一種新型號的毛筆,只需擴展左側(cè)的“抽象部分”,
增加一個新的擴充抽象類;如果需要增加一種新的顏色,只需擴展右側(cè)的“實現(xiàn)部分”,
增加一個新的具體實現(xiàn)類。擴展非常方便,無須修改已有代碼,且不會導(dǎo)致類的數(shù)目增長過快。
對于客戶端而言,可以針對兩個維度的抽象層編程,在程序運行時再動態(tài)確定兩個維度的子類,動態(tài)組合對象,將兩個獨立變化的維度完全解耦,以便能夠靈活地擴充任一維度而對另一維度不造成任何影響。
6. 組合模式
樹形結(jié)構(gòu)的處理——組合模式
樹形結(jié)構(gòu)在軟件中隨處可見,例如操作系統(tǒng)中的目錄結(jié)構(gòu)、應(yīng)用軟件中的菜單、辦公系統(tǒng)中的公司組織結(jié)構(gòu)等等,
如何運用面向?qū)ο蟮姆绞絹硖幚磉@種樹形結(jié)構(gòu)是組合模式需要解決的問題,
組合模式通過一種巧妙的設(shè)計方案使得用戶可以一致性地處理整個樹形結(jié)構(gòu)或者樹形結(jié)構(gòu)的一部分,
也可以一致性地處理樹形結(jié)構(gòu)中的葉子節(jié)點(不包含子節(jié)點的節(jié)點)和容器節(jié)點(包含子節(jié)點的節(jié)點)。
1、什么是組合模式
組合模式,將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),組合模式使得用戶對單個對象和組合對象的使用具有一致性。
組合模式是對樹型數(shù)據(jù)結(jié)構(gòu)的一種實現(xiàn),組合模式主要由兩個角色組成
- 枝結(jié)點
- 葉結(jié)點
所有的組合模式都是由一個根節(jié)點擴展出來的,每個枝結(jié)點都可以擴展出枝結(jié)點和葉結(jié)點。葉結(jié)點表示這條分支的終點,無法擴展。
iOS 中的 View 就是一個標準的組合模式。
- 組合模式的主要優(yōu)點如下:
(1) 組合模式可以清楚地定義分層次的復(fù)雜對象,表示對象的全部或部分層次,它讓客戶端忽略了層次的差異,
方便對整個層次結(jié)構(gòu)進行控制。
(2) 客戶端可以一致地使用一個組合結(jié)構(gòu)或其中單個對象,不必關(guān)心處理的是單個對象還是整個組合結(jié)構(gòu),簡化了客戶端代碼。
(3) 在組合模式中增加新的容器構(gòu)件和葉子構(gòu)件都很方便,無須對現(xiàn)有類庫進行任何修改,符合“開閉原則”。
(4) 組合模式為樹形結(jié)構(gòu)的面向?qū)ο髮崿F(xiàn)提供了一種靈活的解決方案,通過葉子對象和容器對象的遞歸組合,
可以形成復(fù)雜的樹形結(jié)構(gòu),但對樹形結(jié)構(gòu)的控制卻非常簡單。
3.在以下情況下可以考慮使用組合模式:
(1) 在具有整體和部分的層次結(jié)構(gòu)中,希望通過一種方式忽略整體與部分的差異,客戶端可以一致地對待它們。
(2) 在一個使用面向?qū)ο笳Z言開發(fā)的系統(tǒng)中需要處理一個樹形結(jié)構(gòu)。
(3) 在一個系統(tǒng)中能夠分離出葉子對象和容器對象,而且它們的類型不固定,需要增加一些新的類型。
7. 享元模式-
實現(xiàn)對象的復(fù)用- 享元模式
定義:享元模式是 一種 可共享 對象的一種設(shè)計模式。在需要使用大量的細粒度 對象的過程中。這種模式 能很大的減少內(nèi)存和提升性能。
所謂 細粒度,就是對象相近 而且 數(shù)量很多。 我們把這些對象 分成 內(nèi)部狀態(tài) 和 外部狀態(tài)。
內(nèi)部狀態(tài) 只使用一個對象來共享 。
外部狀態(tài) 不能共享出來,是對象區(qū)分的。
大體實現(xiàn):
實現(xiàn)享元模式需要兩個關(guān)鍵組件,通常是可共享的享元對象和保存他們的池。某種中央對象維護這個池,并從它返回適當?shù)膶嵗?
在iOS開發(fā)中,大家肯定都用過UITableViewCell,UICollectionViewCell,
這兩個類在使用過程中就使用了享元模式,工作原理基本就是:利用重用池重用思想,
創(chuàng)建頁面可顯示的cell個數(shù)的對象,在頁面滾動過程中監(jiān)聽每個cell的狀態(tài),從頁面消失的cell被放回重用池,
將要顯示的cell先去重用池中去取,如果可以取到,則繼續(xù)使用這個cell,如果沒有多余的cell,就重新創(chuàng)建新的,
這樣即使你有100條數(shù)據(jù),也僅僅只會創(chuàng)建頁面可顯示個數(shù)的cell對象,
這樣就大大減少了對象的創(chuàng)建,實現(xiàn)了大量內(nèi)存占用,導(dǎo)致內(nèi)存泄露的問題
1.主要優(yōu)點
享元模式的主要優(yōu)點如下:
(1) 可以極大減少內(nèi)存中對象的數(shù)量,使得相同或相似對象在內(nèi)存中只保存一份,從而可以節(jié)約系統(tǒng)資源,提高系統(tǒng)性能。
(2) 享元模式的外部狀態(tài)相對獨立,而且不會影響其內(nèi)部狀態(tài),從而使得享元對象可以在不同的環(huán)境中被共享。
2.主要缺點
享元模式的主要缺點如下:
(1) 享元模式使得系統(tǒng)變得復(fù)雜,需要分離出內(nèi)部狀態(tài)和外部狀態(tài),這使得程序的邏輯復(fù)雜化。
(2) 為了使對象可以共享,享元模式需要將享元對象的部分狀態(tài)外部化,而讀取外部狀態(tài)將使得運行時間變長。
3.適用場景
在以下情況下可以考慮使用享元模式:
(1) 一個系統(tǒng)有大量相同或者相似的對象,造成內(nèi)存的大量耗費。
(2) 對象的大部分狀態(tài)都可以外部化,可以將這些外部狀態(tài)傳入對象中。
(3) 在使用享元模式時需要維護一個存儲享元對象的享元池,而這需要耗費一定的系統(tǒng)資源,因此,
應(yīng)當在需要多次重復(fù)使用享元對象時才值得使用享元模式。
<后續(xù)會持續(xù)更新>