個(gè)人博客地址:Lixuzong's Blog
大部分的Demo可以在這里查看:Demo在這里
首先先講一下大概的設(shè)計(jì)模式的分類。
- 1额各、對(duì)象創(chuàng)建,主要是生成對(duì)象的方法吧恃。
- 2虾啦、接口適配,為了解決兩個(gè)類之間接口不吻合
- 3痕寓、對(duì)象去耦傲醉,對(duì)象去耦合有利于代碼的復(fù)用
- 4、抽象集合呻率,將要用的組合抽離出來(lái)硬毕,主要有組合模式
- 5、行為擴(kuò)展礼仗,在已有功能的基礎(chǔ)上擴(kuò)展功能
- 6吐咳、算法封裝
對(duì)象創(chuàng)建
- 原型
- 工廠方法
- 抽象工廠
- 生成器
- 單例
工廠模式
工廠方法模式: 定義創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類元践,工廠方法使得一個(gè)類的實(shí)例化延遲到其子類韭脊。
首先先看一下類圖:
通過(guò)類圖可以很清楚的看到,有一個(gè)product對(duì)象需要被創(chuàng)建单旁,創(chuàng)建對(duì)象的方法來(lái)自于繼承子類重寫Creator沪羔,也就是說(shuō)需要知道子類的具體類型才能夠進(jìn)行創(chuàng)建。
在《Effective Objective-C 2.0:編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》中也有提及到的虛擬工廠方法象浑,在iOS里面叫做類族蔫饰,比較典型的類就是我們經(jīng)常使用的NSArray類,通常我們會(huì)認(rèn)為NSArray是一個(gè)類愉豺,提供了一些方法篓吁,但是在NSArray類里面獲得確實(shí)其子類的實(shí)例,這就是一個(gè)典型的工廠模式蚪拦。
何時(shí)使用
- 編譯時(shí)無(wú)法準(zhǔn)確預(yù)期要?jiǎng)?chuàng)建的對(duì)象的類越除。
- 類想讓其子類決定在運(yùn)行時(shí)創(chuàng)建什么。
- 類若有若干輔助類為其子類外盯,而你想將返回哪個(gè)子類這一信息局部化。
抽象工程模式
提供一個(gè)創(chuàng)建一系列相關(guān)或者相互依賴的接口的對(duì)象翼雀,而無(wú)需指定他們具體的類饱苟。
通過(guò)類圖我們可以看出,client通過(guò)抽象工廠來(lái)創(chuàng)建ProductA和ProductB狼渊,但是client只要通過(guò)AbstractFactory就可以直接獲得其子類箱熬,而不需要知道子類类垦。這就是為什么是抽象工廠,在工廠方法的基礎(chǔ)上隱藏了其實(shí)現(xiàn)的具體子類城须,在工廠方法的基礎(chǔ)上又進(jìn)行了一次抽象蚤认。
說(shuō)到抽象工廠模式的話,就是要與工廠方法比較一下糕伐。
| 抽象工廠 | 工廠方法 |
| ------------- |: -------------: |
| 通過(guò)對(duì)象組合創(chuàng)建抽象產(chǎn)品 | 通過(guò)類繼承創(chuàng)建抽象產(chǎn)品 |
| 創(chuàng)建多系列產(chǎn)品 | 創(chuàng)建一種產(chǎn)品 |
| 必須修改父類的接口才能支持新的產(chǎn)品 | 子類化創(chuàng)建者并重載工廠方法以創(chuàng)建新的產(chǎn)品 |
假設(shè)你現(xiàn)在正在看Demo的話砰琢,這里可以簡(jiǎn)單的說(shuō)一下。其實(shí)抽象工廠就是在工廠方法的基礎(chǔ)上將具體的類型也隱藏起來(lái)良瞧。所以工廠方法里需要知道之類的類型才能夠創(chuàng)建對(duì)象陪汽,而抽象工廠不需要知道具體的子類的類型就可以直接通過(guò)父類的方法創(chuàng)建子類對(duì)象,所以是在工廠方法的基礎(chǔ)上又抽象了一層褥蚯。
生成器模式
生成器模式:將一個(gè)復(fù)雜對(duì)象的構(gòu)建與他的表現(xiàn)分離挚冤,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表現(xiàn)贩毕。
通過(guò)類圖我們可以看出來(lái)轩拨,Director與一個(gè)builder對(duì)象相識(shí)侨赡,是通過(guò)這個(gè)Builder對(duì)象來(lái)獲取對(duì)象的癞季。
根據(jù)我的理解谆棺,就是單獨(dú)使用一個(gè)類來(lái)管理生成對(duì)象窄驹,client并不需要自己創(chuàng)建對(duì)象钓猬,只需要告訴生成器我需要什么樣的對(duì)象普舆,這樣生成器就會(huì)根據(jù)需求創(chuàng)建響應(yīng)的對(duì)象誊锭。這樣的話就會(huì)將構(gòu)建與表現(xiàn)分離了表悬。Demo在這里
何時(shí)使用
- 需要?jiǎng)?chuàng)建設(shè)計(jì)各種部件的復(fù)雜對(duì)象。創(chuàng)建對(duì)象的算法應(yīng)該獨(dú)立于部件的裝配方式丧靡。常見(jiàn)例子是構(gòu)建組合對(duì)象蟆沫。
- 構(gòu)建過(guò)程需要以不同的方式(例如,部件或表現(xiàn)的不同組合)構(gòu)建對(duì)象温治。
單例模式
單例模式: 保證一個(gè)類只有一個(gè)實(shí)例饭庞,并提供一個(gè)訪問(wèn)他的全局訪問(wèn)點(diǎn)。
單例模式是iOS里面比較常用的設(shè)計(jì)模式熬荆。一般我們平時(shí)要求不是很嚴(yán)格的時(shí)候是這樣寫的:
+ (instancetype)shareCharacter {
static Character *character;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
character = [[self alloc] init];
});
return character;
}
使用 dispatch_once 是為了線程安全舟山,這個(gè)也可以用加鎖來(lái)解決,但一般是交給GCD來(lái)實(shí)現(xiàn)卤恳,會(huì)做部分的優(yōu)化累盗。為什么會(huì)說(shuō)是不嚴(yán)格的單例的,主要是以下兩個(gè)方面:
- 發(fā)起調(diào)用的對(duì)象不能以其他分配方式實(shí)例化單例對(duì)象突琳。否則若债,有可能創(chuàng)建單例的多個(gè)實(shí)例。
- 對(duì)單例對(duì)象實(shí)例化的限制應(yīng)該與引用計(jì)數(shù)內(nèi)存模型共存拆融。
因?yàn)锳RC目前已經(jīng)完全取代了MRC所以第二點(diǎn)也就沒(méi)有必要考慮了蠢琳,但是仍然需要考慮創(chuàng)建對(duì)象的唯一性啊终。也看到有一種方式直接在其他創(chuàng)建對(duì)象的方法中強(qiáng)制拋出異常,個(gè)人認(rèn)為是不可取的傲须。一言不合就貼代碼:
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;
}
這里如要是對(duì)內(nèi)存做了限制蓝牲,首先說(shuō)一下生成對(duì)象的兩種方式 [Character shareCharacter] 和 [[Character alloc] init] 需要讓兩種方法都返回的是同一塊內(nèi)存地址,因?yàn)閛bjective-c采用的是兩段式的初始化方法泰讽,所有只要控制 *+ (instancetype)allocWithZone:(struct _NSZone )zone 返回同樣的內(nèi)存地址就可以了例衍。
這里碰到了一個(gè)疑惑就是在網(wǎng)上看到 - (instancetype)copy {return self;} 的另一個(gè)版本是 - (instancetype)copy {return character;} 就有點(diǎn)困惑self的指代問(wèn)題,通過(guò) [self shareCharacter] 可以看出self指代的是當(dāng)前的類菇绵,怎么在返回的時(shí)候是一個(gè)對(duì)象那肄渗?后來(lái)查了下資料并結(jié)合ios runtime的特性,原來(lái)self是在運(yùn)行的時(shí)候動(dòng)態(tài)決定其指代的咬最,可以是當(dāng)前類翎嫡,也可以是當(dāng)前的對(duì)象。那么這里的理解就是在類方法里面就是當(dāng)前類永乌,在實(shí)例方法里面就是當(dāng)前對(duì)象惑申。
接口適配
- 適配器
- 橋接
- 外觀
適配器模式
適配器模式:將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另一個(gè)接口。適配器模式使得原本由于接口不兼容而不能一起工作的類可以一起工作
適配器模式在ios里面的實(shí)現(xiàn)分為兩種翅雏,分別是類適配器和對(duì)象適配器圈驼。假設(shè)我們有A類,B類望几,把A類當(dāng)成客戶绩脆,但是A類不能直接調(diào)用B類的接口,所以需要一個(gè)適配器C類橄抹。
- 類適配器:將適配器C類繼承于B類靴迫,對(duì)A暴露相關(guān)功能的接口。
- 對(duì)象適配器:適配器C類擁有B類的實(shí)例楼誓,對(duì)A暴露相關(guān)功能的接口玉锌。
在使用的過(guò)程中不使用適配器模式也能實(shí)現(xiàn)功能,A類直接擁有B類的對(duì)象疟羹,將參數(shù)傳給B類也是能夠?qū)崿F(xiàn)功能的主守,但是就形成了強(qiáng)耦合關(guān)系,使用適配器模式主要是為了解耦合榄融。在實(shí)際應(yīng)用中我發(fā)現(xiàn)已經(jīng)不自覺(jué)地使用了該設(shè)計(jì)模式参淫,也算是比較常用的設(shè)計(jì)模式,只是這里抽象出來(lái)了而已愧杯。
delegate是對(duì)象適配器
如果用delegate來(lái)實(shí)現(xiàn)適配器模式的話黄刚,那么從分類上來(lái)說(shuō)應(yīng)該是對(duì)象適配器,Demo這里就沒(méi)有寫民效,一般我們使用delegate的時(shí)候很大部分情況是適配器模式憔维。
Block實(shí)現(xiàn)對(duì)象適配器
使用delegate的方法都是可以用block來(lái)改寫了,那么這里就不多說(shuō)了畏邢。
何時(shí)使用
- 已有類的接口與需求不匹配业扒。
- 想要一個(gè)可復(fù)用的類,該類能夠同可能帶有不兼容接口的其他類協(xié)作舒萎。
- 需要適配一個(gè)類的幾個(gè)不同子類程储,可是讓每一個(gè)子類去子類化一個(gè)類適配器又不實(shí)現(xiàn)。那么可以使用對(duì)象適配器(也叫委托)來(lái)適配其父類的接口臂寝。
橋接模式
將抽象部分與他的實(shí)現(xiàn)部分分離章鲤,使他們可以獨(dú)立的變化。
橋接模式簡(jiǎn)單的說(shuō)就是將抽象部分與實(shí)現(xiàn)部分分離咆贬,但是根據(jù)不同的需求有很多不同的是實(shí)現(xiàn)方式败徊,使用起來(lái)比較靈活。Demo地址在這掏缎。與書上的代碼稍微有些差異皱蹦,因?yàn)橥ㄟ^(guò)抽象父類來(lái)調(diào)用具體子類的方式上有疑問(wèn),這里是給父類添加了一個(gè)初始化方法眷蜈,在這個(gè)方法里面直接返回子類的對(duì)象沪哺。(有點(diǎn)像工廠模式)。
何時(shí)使用
- 不想在抽象與其實(shí)現(xiàn)之間形成固定的綁定關(guān)系(這樣就能在運(yùn)行時(shí)切換實(shí)現(xiàn))酌儒。
- 抽象及其實(shí)現(xiàn)都應(yīng)通過(guò)子類化獨(dú)立進(jìn)行擴(kuò)展辜妓。
- 對(duì)抽象的實(shí)現(xiàn)進(jìn)行修改不應(yīng)該影響客戶端代碼。
- 如果每個(gè)實(shí)現(xiàn)需要額外的子類以細(xì)化抽象忌怎,則說(shuō)明有必要把他們分成兩個(gè)部分籍滴。
- 想要帶有不同抽象接口的多個(gè)對(duì)象之間共享一個(gè)實(shí)現(xiàn)。
外觀模式
為系統(tǒng)中的一組接口提供一個(gè)統(tǒng)一的接口呆躲。外觀定義一個(gè)高層接口异逐,讓子系統(tǒng)更容易使用。
簡(jiǎn)單的說(shuō)就是將一組功能抽象出來(lái)插掂,只對(duì)外暴露一個(gè)接口灰瞻。高度的抽象。
何時(shí)使用
- 子系統(tǒng)正在逐漸變得復(fù)雜辅甥。應(yīng)用模式過(guò)程中演化出許多類酝润。可以使用外觀為這些子系統(tǒng)提供給一個(gè)較簡(jiǎn)單的接口璃弄。
- 可以使用外觀對(duì)子系統(tǒng)分層要销。每個(gè)子系統(tǒng)級(jí)別有一個(gè)外觀作為入口點(diǎn)。讓它們通過(guò)其外觀進(jìn)行通信夏块,可以簡(jiǎn)化他們的依賴關(guān)系疏咐。
對(duì)象去耦
- 中介者
- 觀察者
中介者模式
中介者模式: 用一個(gè)對(duì)象來(lái)封裝一系列對(duì)象的交互方式纤掸。中介者使得個(gè)對(duì)象不需要顯示的相互引用,從而使得耦合松散浑塞,而且可以獨(dú)立地改變他們之間的交互借跪。
簡(jiǎn)單的說(shuō)就是單獨(dú)使用一個(gè)對(duì)象C來(lái)管理A對(duì)象和B對(duì)象之間的交互或者說(shuō)將A和B中較為復(fù)雜的邏輯抽象出來(lái)。減少A和B之間的耦合酌壕,便于A和B對(duì)象的重用掏愁。
何時(shí)使用中介者模式
- 對(duì)象之間的交互雖然定義明確但是非常復(fù)雜,導(dǎo)致一組對(duì)象項(xiàng)目依賴并且難以理解卵牍。
- 因?yàn)閷?duì)象引用了許多其他對(duì)象并與其通信果港,導(dǎo)致對(duì)象難以復(fù)用。
- 想要定制一個(gè)分布在多個(gè)類中的邏輯或行為糊昙,又不想生成太多的子類辛掠。
觀察者模式
定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生變化時(shí)溅蛉,所有依賴于它的對(duì)象都得到通知并自動(dòng)更新公浪。
觀察者模式在iOS里面的使用頻率比較多,框架也已經(jīng)幫我們實(shí)現(xiàn)了方便使用的方式船侧。首先是NSNotificationCenter欠气,再者還有KVO,所以基本已經(jīng)可以滿足我們的需求镜撩。目的就是解除觀察者和被觀察者之間的關(guān)系预柒。
抽象集合
- 組合
- 迭代器
組合模式(Composite)
將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。組合使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性袁梗。
我們可以看到如上圖所示的一個(gè)樹(shù)形就夠就是組合模式宜鸯,在這個(gè)樹(shù)形結(jié)構(gòu)當(dāng)中,線段既包含節(jié)點(diǎn)遮怜,也包換線段淋袖,但是client使用的時(shí)候并不關(guān)心Stroke包含什么,只是想要統(tǒng)一的處理锯梁。所以組合模式可以對(duì)外操作保持一致性即碗。
在CocoaTouch中一個(gè)非常典型的例子就是UIView。UIView可以嵌套UIView陌凳,Client只需要對(duì)父View做響應(yīng)的處理剥懒,父View就可以遞歸的調(diào)用子View,像渲染視圖的命令合敦,父View接收到只之后就會(huì)逐級(jí)向下傳遞初橘。
迭代器
提供一種方法順序訪問(wèn)一個(gè)聚合對(duì)象中的各個(gè)元素,而又不需要暴露該對(duì)象的內(nèi)部表示
蘋果公司用自己的命名規(guī)則“枚舉器/枚舉”改寫了迭代器模式,用于相關(guān)基礎(chǔ)類的方法保檐「酰基礎(chǔ)框架中的NSEnumerator類實(shí)現(xiàn)了迭代器模式。NSArray展东,NSSet赔硫,NSDictionary這樣的集合類,定義了返回與集合的類型想響應(yīng)的NSEnumerator子類實(shí)例的方法盐肃。用我這種比較通俗的說(shuō)法就是,提供了一種遍歷集合類的方法权悟。也就是迭代器模式砸王。
基本上有兩種迭代器:外部迭代器和內(nèi)部迭代器。外部迭代器讓client直接操作迭代過(guò)程峦阁,所以client需要知道外部迭代器才能使用谦铃。另一種情況是,集合對(duì)象(被迭代的目標(biāo)對(duì)象)在其內(nèi)部維護(hù)并操作了一個(gè)外部迭代器榔昔。提供內(nèi)部迭代器的典型的集合對(duì)象為client定義一個(gè)接口驹闰,或者從底層集合一次訪問(wèn)一個(gè)元素,或者向每個(gè)元素發(fā)送消息撒会。比如NSArray 的 - (void)makeObjectsPerformSelector:(SEL)aSelector 就是內(nèi)部迭代器嘹朗。就是不需要知道迭代器,在內(nèi)部都遍歷并實(shí)現(xiàn)操作诵肛。
何時(shí)使用
- 需要訪問(wèn)組合對(duì)象的內(nèi)容屹培,而又不暴露內(nèi)部表示。
- 需要通過(guò)多種方式暴露組合對(duì)象怔檩。
- 需要提供一個(gè)統(tǒng)一的接口褪秀,來(lái)遍歷各種類型的組合對(duì)象。
行為擴(kuò)展
- 訪問(wèn)者模式
- 裝飾
- 責(zé)任鏈
訪問(wèn)者模式
表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作薛训。它讓我們可以在不改變各元素類的前提下定義作用于這些元素的新操作媒吗。
解釋一下訪問(wèn)者模式,就是將自己不熟悉的業(yè)務(wù)承包出去乙埃。比如說(shuō)我們的家是一個(gè)類闸英,對(duì)于里面的下水道我們就有各種使用的方法,但是關(guān)于修理下水道我們要承包出去膊爪,找專業(yè)的人來(lái)對(duì)他進(jìn)行操作自阱,而這個(gè)維修的操做不能影響我們現(xiàn)在的使用。所以這就是訪問(wèn)者模式米酬,承包商就是訪問(wèn)者沛豌。這樣比較容易理解。
從類圖上可以看出來(lái),Element調(diào)用 acceptVister: 方法將自身傳遞給vister加派,vister接收之后就可以對(duì)其進(jìn)行操作叫确。從而可以獲得行為的擴(kuò)展。
何時(shí)使用訪問(wèn)者模式
- 一個(gè)復(fù)雜的對(duì)象結(jié)構(gòu)包含很多其他的對(duì)象芍锦,它們有不同的接口(比如組合體)竹勉,但是相對(duì)這些對(duì)象實(shí)施一些依賴其具體類型的操作。
- 需要對(duì)一個(gè)組合結(jié)構(gòu)中的對(duì)象進(jìn)行很多不相關(guān)的操作娄琉,但是不想讓這些操作“污染”這些對(duì)象的類次乓。可以將相關(guān)的操作集中起來(lái)孽水,定義在一個(gè)訪問(wèn)者類中票腰,并在需要在訪問(wèn)者中定義的操作時(shí)使用它。
- 定義復(fù)雜結(jié)構(gòu)的類很少作修改女气,但是經(jīng)常需要向其添加新的操作杏慰。