個人博客地址:Lixuzong's Blog
大部分的Demo可以在這里查看:Demo在這里
首先先講一下大概的設計模式的分類。
- 1、對象創(chuàng)建,主要是生成對象的方法。
- 2、接口適配,為了解決兩個類之間接口不吻合
- 3、對象去耦,對象去耦合有利于代碼的復用
- 4、抽象集合,將要用的組合抽離出來,主要有組合模式
- 5、行為擴展,在已有功能的基礎上擴展功能
- 6、算法封裝

對象創(chuàng)建
- 原型
- 工廠方法
- 抽象工廠
- 生成器
- 單例
工廠模式
工廠方法模式: 定義創(chuàng)建對象的接口,讓子類決定實例化哪一個類,工廠方法使得一個類的實例化延遲到其子類。
首先先看一下類圖:

通過類圖可以很清楚的看到,有一個product對象需要被創(chuàng)建,創(chuàng)建對象的方法來自于繼承子類重寫Creator,也就是說需要知道子類的具體類型才能夠進行創(chuàng)建。
在《Effective Objective-C 2.0:編寫高質量iOS與OS X代碼的52個有效方法》中也有提及到的虛擬工廠方法,在iOS里面叫做類族,比較典型的類就是我們經常使用的NSArray類,通常我們會認為NSArray是一個類,提供了一些方法,但是在NSArray類里面獲得確實其子類的實例,這就是一個典型的工廠模式。
何時使用
- 編譯時無法準確預期要創(chuàng)建的對象的類。
- 類想讓其子類決定在運行時創(chuàng)建什么。
- 類若有若干輔助類為其子類,而你想將返回哪個子類這一信息局部化。
抽象工程模式
提供一個創(chuàng)建一系列相關或者相互依賴的接口的對象,而無需指定他們具體的類。

通過類圖我們可以看出,client通過抽象工廠來創(chuàng)建ProductA和ProductB,但是client只要通過AbstractFactory就可以直接獲得其子類,而不需要知道子類。這就是為什么是抽象工廠,在工廠方法的基礎上隱藏了其實現(xiàn)的具體子類,在工廠方法的基礎上又進行了一次抽象。
說到抽象工廠模式的話,就是要與工廠方法比較一下。
| 抽象工廠 | 工廠方法 |
| ------------- |: -------------: |
| 通過對象組合創(chuàng)建抽象產品 | 通過類繼承創(chuàng)建抽象產品 |
| 創(chuàng)建多系列產品 | 創(chuàng)建一種產品 |
| 必須修改父類的接口才能支持新的產品 | 子類化創(chuàng)建者并重載工廠方法以創(chuàng)建新的產品 |
假設你現(xiàn)在正在看Demo的話,這里可以簡單的說一下。其實抽象工廠就是在工廠方法的基礎上將具體的類型也隱藏起來。所以工廠方法里需要知道之類的類型才能夠創(chuàng)建對象,而抽象工廠不需要知道具體的子類的類型就可以直接通過父類的方法創(chuàng)建子類對象,所以是在工廠方法的基礎上又抽象了一層。
生成器模式
生成器模式:將一個復雜對象的構建與他的表現(xiàn)分離,使得同樣的構建過程可以創(chuàng)建不同的表現(xiàn)。

通過類圖我們可以看出來,Director與一個builder對象相識,是通過這個Builder對象來獲取對象的。
根據(jù)我的理解,就是單獨使用一個類來管理生成對象,client并不需要自己創(chuàng)建對象,只需要告訴生成器我需要什么樣的對象,這樣生成器就會根據(jù)需求創(chuàng)建響應的對象。這樣的話就會將構建與表現(xiàn)分離了。Demo在這里
何時使用
- 需要創(chuàng)建設計各種部件的復雜對象。創(chuàng)建對象的算法應該獨立于部件的裝配方式。常見例子是構建組合對象。
- 構建過程需要以不同的方式(例如,部件或表現(xiàn)的不同組合)構建對象。
單例模式
單例模式: 保證一個類只有一個實例,并提供一個訪問他的全局訪問點。

單例模式是iOS里面比較常用的設計模式。一般我們平時要求不是很嚴格的時候是這樣寫的:
+ (instancetype)shareCharacter {
static Character *character;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
character = [[self alloc] init];
});
return character;
}
使用 dispatch_once 是為了線程安全,這個也可以用加鎖來解決,但一般是交給GCD來實現(xiàn),會做部分的優(yōu)化。為什么會說是不嚴格的單例的,主要是以下兩個方面:
- 發(fā)起調用的對象不能以其他分配方式實例化單例對象。否則,有可能創(chuàng)建單例的多個實例。
- 對單例對象實例化的限制應該與引用計數(shù)內存模型共存。
因為ARC目前已經完全取代了MRC所以第二點也就沒有必要考慮了,但是仍然需要考慮創(chuàng)建對象的唯一性。也看到有一種方式直接在其他創(chuàng)建對象的方法中強制拋出異常,個人認為是不可取的。一言不合就貼代碼:
static Character *character;
+ (instancetype)shareCharacter {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
character = [[super allocWithZone:NULL] init];
});
return character;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self shareCharacter];
}
- (instancetype)copy {
return self;
}
這里如要是對內存做了限制,首先說一下生成對象的兩種方式 [Character shareCharacter] 和 [[Character alloc] init] 需要讓兩種方法都返回的是同一塊內存地址,因為objective-c采用的是兩段式的初始化方法,所有只要控制 *+ (instancetype)allocWithZone:(struct _NSZone )zone 返回同樣的內存地址就可以了。
這里碰到了一個疑惑就是在網(wǎng)上看到 - (instancetype)copy {return self;} 的另一個版本是 - (instancetype)copy {return character;} 就有點困惑self的指代問題,通過 [self shareCharacter] 可以看出self指代的是當前的類,怎么在返回的時候是一個對象那?后來查了下資料并結合ios runtime的特性,原來self是在運行的時候動態(tài)決定其指代的,可以是當前類,也可以是當前的對象。那么這里的理解就是在類方法里面就是當前類,在實例方法里面就是當前對象。
接口適配
- 適配器
- 橋接
- 外觀
適配器模式
適配器模式:將一個類的接口轉換成客戶希望的另一個接口。適配器模式使得原本由于接口不兼容而不能一起工作的類可以一起工作
適配器模式在ios里面的實現(xiàn)分為兩種,分別是類適配器和對象適配器。假設我們有A類,B類,把A類當成客戶,但是A類不能直接調用B類的接口,所以需要一個適配器C類。
- 類適配器:將適配器C類繼承于B類,對A暴露相關功能的接口。

- 對象適配器:適配器C類擁有B類的實例,對A暴露相關功能的接口。

在使用的過程中不使用適配器模式也能實現(xiàn)功能,A類直接擁有B類的對象,將參數(shù)傳給B類也是能夠實現(xiàn)功能的,但是就形成了強耦合關系,使用適配器模式主要是為了解耦合。在實際應用中我發(fā)現(xiàn)已經不自覺地使用了該設計模式,也算是比較常用的設計模式,只是這里抽象出來了而已。
delegate是對象適配器
如果用delegate來實現(xiàn)適配器模式的話,那么從分類上來說應該是對象適配器,Demo這里就沒有寫,一般我們使用delegate的時候很大部分情況是適配器模式。
Block實現(xiàn)對象適配器
使用delegate的方法都是可以用block來改寫了,那么這里就不多說了。
何時使用
- 已有類的接口與需求不匹配。
- 想要一個可復用的類,該類能夠同可能帶有不兼容接口的其他類協(xié)作。
- 需要適配一個類的幾個不同子類,可是讓每一個子類去子類化一個類適配器又不實現(xiàn)。那么可以使用對象適配器(也叫委托)來適配其父類的接口。
橋接模式
將抽象部分與他的實現(xiàn)部分分離,使他們可以獨立的變化。

橋接模式簡單的說就是將抽象部分與實現(xiàn)部分分離,但是根據(jù)不同的需求有很多不同的是實現(xiàn)方式,使用起來比較靈活。Demo地址在這。與書上的代碼稍微有些差異,因為通過抽象父類來調用具體子類的方式上有疑問,這里是給父類添加了一個初始化方法,在這個方法里面直接返回子類的對象。(有點像工廠模式)。
何時使用
- 不想在抽象與其實現(xiàn)之間形成固定的綁定關系(這樣就能在運行時切換實現(xiàn))。
- 抽象及其實現(xiàn)都應通過子類化獨立進行擴展。
- 對抽象的實現(xiàn)進行修改不應該影響客戶端代碼。
- 如果每個實現(xiàn)需要額外的子類以細化抽象,則說明有必要把他們分成兩個部分。
- 想要帶有不同抽象接口的多個對象之間共享一個實現(xiàn)。
外觀模式
為系統(tǒng)中的一組接口提供一個統(tǒng)一的接口。外觀定義一個高層接口,讓子系統(tǒng)更容易使用。
簡單的說就是將一組功能抽象出來,只對外暴露一個接口。高度的抽象。
何時使用
- 子系統(tǒng)正在逐漸變得復雜。應用模式過程中演化出許多類??梢允褂猛庥^為這些子系統(tǒng)提供給一個較簡單的接口。
- 可以使用外觀對子系統(tǒng)分層。每個子系統(tǒng)級別有一個外觀作為入口點。讓它們通過其外觀進行通信,可以簡化他們的依賴關系。
對象去耦
- 中介者
- 觀察者
中介者模式
中介者模式: 用一個對象來封裝一系列對象的交互方式。中介者使得個對象不需要顯示的相互引用,從而使得耦合松散,而且可以獨立地改變他們之間的交互。

簡單的說就是單獨使用一個對象C來管理A對象和B對象之間的交互或者說將A和B中較為復雜的邏輯抽象出來。減少A和B之間的耦合,便于A和B對象的重用。
何時使用中介者模式
- 對象之間的交互雖然定義明確但是非常復雜,導致一組對象項目依賴并且難以理解。
- 因為對象引用了許多其他對象并與其通信,導致對象難以復用。
- 想要定制一個分布在多個類中的邏輯或行為,又不想生成太多的子類。
觀察者模式
定義對象間的一種一對多的依賴關系,當一個對象的狀態(tài)發(fā)生變化時,所有依賴于它的對象都得到通知并自動更新。

觀察者模式在iOS里面的使用頻率比較多,框架也已經幫我們實現(xiàn)了方便使用的方式。首先是NSNotificationCenter,再者還有KVO,所以基本已經可以滿足我們的需求。目的就是解除觀察者和被觀察者之間的關系。
抽象集合
- 組合
- 迭代器
組合模式(Composite)
將對象組合成樹形結構以表示“部分-整體”的層次結構。組合使得用戶對單個對象和組合對象的使用具有一致性。

我們可以看到如上圖所示的一個樹形就夠就是組合模式,在這個樹形結構當中,線段既包含節(jié)點,也包換線段,但是client使用的時候并不關心Stroke包含什么,只是想要統(tǒng)一的處理。所以組合模式可以對外操作保持一致性。
在CocoaTouch中一個非常典型的例子就是UIView。UIView可以嵌套UIView,Client只需要對父View做響應的處理,父View就可以遞歸的調用子View,像渲染視圖的命令,父View接收到只之后就會逐級向下傳遞。
迭代器
提供一種方法順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示
蘋果公司用自己的命名規(guī)則“枚舉器/枚舉”改寫了迭代器模式,用于相關基礎類的方法?;A框架中的NSEnumerator類實現(xiàn)了迭代器模式。NSArray,NSSet,NSDictionary這樣的集合類,定義了返回與集合的類型想響應的NSEnumerator子類實例的方法。用我這種比較通俗的說法就是,提供了一種遍歷集合類的方法。也就是迭代器模式。
基本上有兩種迭代器:外部迭代器和內部迭代器。外部迭代器讓client直接操作迭代過程,所以client需要知道外部迭代器才能使用。另一種情況是,集合對象(被迭代的目標對象)在其內部維護并操作了一個外部迭代器。提供內部迭代器的典型的集合對象為client定義一個接口,或者從底層集合一次訪問一個元素,或者向每個元素發(fā)送消息。比如NSArray 的 - (void)makeObjectsPerformSelector:(SEL)aSelector 就是內部迭代器。就是不需要知道迭代器,在內部都遍歷并實現(xiàn)操作。
何時使用
- 需要訪問組合對象的內容,而又不暴露內部表示。
- 需要通過多種方式暴露組合對象。
- 需要提供一個統(tǒng)一的接口,來遍歷各種類型的組合對象。
行為擴展
- 訪問者模式
- 裝飾
- 責任鏈
訪問者模式
表示一個作用于某對象結構中的各元素的操作。它讓我們可以在不改變各元素類的前提下定義作用于這些元素的新操作。

解釋一下訪問者模式,就是將自己不熟悉的業(yè)務承包出去。比如說我們的家是一個類,對于里面的下水道我們就有各種使用的方法,但是關于修理下水道我們要承包出去,找專業(yè)的人來對他進行操作,而這個維修的操做不能影響我們現(xiàn)在的使用。所以這就是訪問者模式,承包商就是訪問者。這樣比較容易理解。
從類圖上可以看出來,Element調用 acceptVister: 方法將自身傳遞給vister,vister接收之后就可以對其進行操作。從而可以獲得行為的擴展。
何時使用訪問者模式
- 一個復雜的對象結構包含很多其他的對象,它們有不同的接口(比如組合體),但是相對這些對象實施一些依賴其具體類型的操作。
- 需要對一個組合結構中的對象進行很多不相關的操作,但是不想讓這些操作“污染”這些對象的類。可以將相關的操作集中起來,定義在一個訪問者類中,并在需要在訪問者中定義的操作時使用它。
- 定義復雜結構的類很少作修改,但是經常需要向其添加新的操作。