設(shè)計模式理解
設(shè)計模式一般會問你在項目中常用的設(shè)計模式有那些哼凯?等你說完之后會問你怎么用的欲间,什么場景用的?它的優(yōu)缺點是什么断部?遵循了什么原則猎贴?違背了什么原則?...
UML類圖
依賴是弱關(guān)聯(lián):只是使用到了
關(guān)聯(lián):需要引用蝴光,表示擁有關(guān)系她渴,需要添加屬性
聚合關(guān)系:也是一種關(guān)聯(lián)關(guān)系,跟關(guān)聯(lián)代碼表現(xiàn)是相同的蔑祟,只不過在語意上有區(qū)別趁耗,關(guān)聯(lián)關(guān)系的對象間是相互獨立的,而聚合關(guān)系的對象之間存在包容關(guān)系疆虚。
組合關(guān)系:也是一種關(guān)聯(lián)關(guān)系苛败,耦合度比聚合關(guān)系更強,表現(xiàn)的是整體-部分的關(guān)系径簿,而且整體負責部分的生命周期罢屈,部分是不暴露給外部,而且部分不能脫離整體單獨使用牍帚。
聚合(空心菱形)和組合(實心菱形)的區(qū)別
聚合關(guān)系中儡遮,被聚合的類的構(gòu)造函數(shù)必須帶聚合類形參乳蛾,聚合類可以獨立于被聚合類而存在暗赶,例如一只鳥可以獨立于鳥群而生存鄙币;組合關(guān)系中,被組合類的構(gòu)造函數(shù)中必須先一步構(gòu)造組合類蹂随,后者是前者構(gòu)造出來的必要條件十嘿,是其一部分,不能獨立存在岳锁,例如翅膀不能獨立于鳥而生存绩衷。
設(shè)計原則
1、單一指責原則:一個類只需要有一個職責激率,如果包含了多個職責咳燕,需要拆分成多個類來管理。
2乒躺、開閉原則:對修改封閉招盲,對擴展開放。也就是盡量不要修改類的內(nèi)部代碼嘉冒,可以使用分類來代替曹货,或者使用繼承。
3讳推、接口隔離原則:不同的功能需要定義成不同的接口顶籽,而不要把所有的功能都放在一個接口中,防止接口冗余银觅。
4礼饱、里氏替換原則:所有使用父類的地方都可以使用子類來替換,所以就是盡量少重寫父類的方法究驴,子類可以增加方法慨仿。
5、依賴倒置原則:使用者應(yīng)該盡量不依賴實現(xiàn)者纳胧,他們都應(yīng)該依賴實現(xiàn)者的抽象镰吆。
6、迪米特法則:一個對象應(yīng)該對其他對象保持最少的了解跑慕,實現(xiàn)低耦合万皿,高內(nèi)聚。
7核行、組合/聚合模式:少用繼承牢硅,多用組合關(guān)系來實現(xiàn)(比如一個人的職業(yè),person組合了職業(yè)類而不是職繼承了人)
13種常用的設(shè)計模式:
1芝雪、適配器模式?
2减余、策略模式
3、觀察者模式
4惩系、原型模式/外觀模式?
5位岔、裝飾模式?
6如筛、工廠模式?
7、橋接模式?
8抒抬、代理模式?
9杨刨、單例模式?
10、備忘錄模式
11擦剑、建造者模式(生成器模式)
12妖胀、命令模式?
13、組合模式?
一惠勒、創(chuàng)建型模式:主要用于創(chuàng)建對象
1.在軟件工程中赚抡,創(chuàng)建型模式是處理對象創(chuàng)建的設(shè)計模式,試圖根據(jù)實際情況使用合適的方式創(chuàng)建對象纠屋∨缕罚基本的對象創(chuàng)建方式可能會導致設(shè)計上的問題,或增加設(shè)計的復雜度巾遭。創(chuàng)建型模式通過以某種方式控制對象的創(chuàng)建來解決問題肉康。
2.創(chuàng)建型模式由兩個主導思想構(gòu)成。一是將系統(tǒng)使用的具體類封裝起來灼舍,二是隱藏這些具體類的實例創(chuàng)建和結(jié)合的方式吼和。
3.創(chuàng)建型模式旨在將系統(tǒng)與它的對象創(chuàng)建、結(jié)合骑素、表示的方式分離炫乓。這些設(shè)計模式在對象創(chuàng)建的類型、主體献丑、方式末捣、時間等方面提高了系統(tǒng)的靈活性
***工廠方法:提供簡單方便的創(chuàng)建對象的方法,定義一個創(chuàng)建對象的接口创橄,由其子類去實例化要創(chuàng)建的類
1箩做、簡單工廠:工廠類、抽象產(chǎn)品類妥畏、產(chǎn)品1邦邦、產(chǎn)品2 creatWithType來創(chuàng)建產(chǎn)品
2、工廠方法類:抽象工廠creat醉蚁、工廠1燃辖、工廠2、抽象產(chǎn)品网棍、產(chǎn)品1黔龟、產(chǎn)品2? fac1? creat、fac2 creat
3、抽象工廠:抽象工廠 creatA creatB 工廠1氏身、工廠2巍棱、抽象產(chǎn)品A、產(chǎn)品A1观谦、產(chǎn)品A2、抽象產(chǎn)品B桨菜、產(chǎn)品B1豁状、產(chǎn)品B2
實際使用場景:微信支付、支付寶支付
工廠方法和抽象工廠區(qū)別:
1倒得、工廠方法只有一個抽象產(chǎn)品泻红,抽象工廠有多個抽象產(chǎn)品
2、工廠方法的具體工廠只能創(chuàng)建一個具體產(chǎn)品類實例霞掺,抽象工廠的具體工廠可以創(chuàng)建多個具體產(chǎn)品實例
3谊路、工廠方法是由工廠子類自行決定實例化哪個類,抽象工廠是自己決定實例化哪個類菩彬。
***建造者模式:
也是提供簡單創(chuàng)建產(chǎn)品的方法缠劝,不過他是用來創(chuàng)建復雜的產(chǎn)品。把產(chǎn)品的創(chuàng)建過程(在Director中)和產(chǎn)品的表現(xiàn)(在builder中)分離骗灶。相當于工廠模式的擴展惨恭。工廠模式是把創(chuàng)建產(chǎn)品的邏輯放在工廠中,而建造者模式是增加了一個director耙旦,建造者類相當于工廠脱羡,生產(chǎn)產(chǎn)品組件,director內(nèi)部將建造者創(chuàng)建產(chǎn)品的過程進行組合免都,并返回產(chǎn)品锉罐。
***原型模式:
應(yīng)用于“復制”操作的模式,就是原型模式绕娘,也就是我們所說的copy脓规,不過這里的復制是深復制,是復制原對象险领,并開辟新的內(nèi)存區(qū)域抖拦。從功能角度來看,只要復制自身比重新實例化都要好舷暮,就應(yīng)該使用原型模式态罪。
OC中通過NSCopying協(xié)議,以及實現(xiàn)copyWithZone方法下面,實現(xiàn)對象的復制
舉例:
-(void)viewDidLoad {
? ? [super viewDidLoad];
? ? YZPerson *p1 =[[YZPerson alloc]init];
? ? p1.name = @"jack";
? ? p1.age = 10;
? ? YZPerson * p2 =[p1 copy];
? ? p2.name = @"rose";
}
@interface YZPerson : NSObject <NSCopying>
@end
#import "YZPerson.h"
@implementation YZPerson
-(id)copyWithZone:(nullable NSZone *)zone{
? ? YZPerson *p =[[YZPerson allocWithZone:zone]init];
? ? p.name = self.name;
? ? p.age = self.age;
? ? return p;
}
@end
現(xiàn)實使用實例:比如我傳一個對象到一個控制器复颈,如果直接傳對象過去,然后另一個控制器中修改了對象的內(nèi)容,則另一個控制器的內(nèi)容也會因為對象的改變而改變耗啦,所以凿菩,需要通過copy重新復制一個對象進行傳遞
***單例模式:
保證在整個程序運行中,只會初始化一次帜讲,只會存在一個內(nèi)存衅谷。并且提供該實例的全局訪問方式
手寫單例
Static Singleton *instance = nil;
+(instancetype)sharedSingleton {
if(instance == nil){
instance =[[self alloc]init];
}
}
+(instancetype)copyWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
instance =[super allocWithZone:zone];
});
return instance;
}
-(id)copy {
return self;
}
-(id)mutableCopy {
return self;
}
二、結(jié)構(gòu)型模式:用于處理類或?qū)ο蟮慕M合
***適配器模式:
將一個類的接口轉(zhuǎn)換成客戶端希望的接口似将,適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作获黔,分為類適配器,和對象適配器
類適配器:
使用的繼承適配者
對象適配器:
對適配者只是依賴
使用場景:
1在验、delegate就是使用的適配者模式玷氏,比如UITableView就是相當于使用者(客戶端),他的delegate對象就是適配者腋舌,需要實現(xiàn)UITableview的協(xié)議盏触,被適配者就是UI控件,需要又代理對象對UI控件進行組合成UITableviewCell進行返回
2块饺、對一些第三方庫的封裝也是使用的適配者模式赞辩,比如對網(wǎng)絡(luò)請求的簡單封裝,客戶端想要發(fā)送一個post請求授艰,首先定義協(xié)議類提供post方法诗宣,然后創(chuàng)建網(wǎng)絡(luò)管理的adapter,netAdapter想诅,實現(xiàn)post方法調(diào)用第三方庫(adaptee被適配者)的post相關(guān)方法
***橋接模式:
將抽象與實現(xiàn)部分分離召庞,使他們都可以獨立的變化
使用場景:抽象和實現(xiàn)都可以通過子類獨立進行擴展
1、不想在抽象與其實現(xiàn)之間形成固定的綁定關(guān)系来破,這樣就能在運行時切換實現(xiàn)篮灼。
2、抽象及其實現(xiàn)都可以通過子類化獨立進行擴展
3徘禁、對抽象的實現(xiàn)進行修改不影響客戶端代碼
4诅诱、如果每個實現(xiàn)需要額外的子類以細化抽象,則說明有必要把他們分成兩部分
5送朱、想在帶有不同抽象接口的多個對象之間共享實現(xiàn)
組成部分:
1娘荡、抽象接口的抽象類
2、抽象接口的子類
3驶沼、抽象實現(xiàn)類
4炮沐、抽象實現(xiàn)類的子類
具體應(yīng)用
具體例子:jsBridge,實現(xiàn)原生與h5進行交互回怜,是雙向橋
***組合模式:
這種模式將互相關(guān)聯(lián)的對象合成為樹形結(jié)構(gòu)大年,以表現(xiàn)部分-全部的層次結(jié)構(gòu)。而且讓客戶端可以統(tǒng)一的單獨處理部分與整體對象。
組成部分:
抽象接口:提供公用的方法和屬性
實現(xiàn)類:使用一個數(shù)組聚合抽象接口對象
UML圖
使用翔试,可以使用組合模式創(chuàng)建樹形結(jié)構(gòu)轻要,以及遍歷該結(jié)構(gòu) 文件系統(tǒng)的創(chuàng)建和遍歷
示例代碼:
1、使用OC實現(xiàn)二叉樹(2才是組合模式的示例)
2垦缅、組合模式
***裝飾者模式:
是一種動態(tài)擴展對象功能的模式冲泥,需要對被裝飾者進行引用實例化對象”谙眩或者內(nèi)部包含被裝飾者的方法凡恍,然后在該方法中調(diào)用被裝飾者的方法,并且可以添加一些自己的邏輯粹庞。
當然也可以通過分類來實現(xiàn)
標準的裝飾者模式UML圖
比如我們要裝飾一個image咳焚,首先需要引用image洽损,并且包含image屬性庞溜,添加一個move方法,調(diào)用時候傳入image碑定,并調(diào)用裝飾者的move方法流码。
***外觀模式:
為子系統(tǒng)中一組不同的接口提供統(tǒng)一的接口。外觀定義了上層接口延刘,通過降低復雜度和隱藏子系統(tǒng)間的通信及依存關(guān)系漫试,讓子系統(tǒng)更易于使用。
其實也就是一種封裝方式碘赖。
***享元模式:
分享元素驾荣,分享內(nèi)存,以達到復用的目的普泡。
享元模式是有一個享元池播掷,享元池中每一組享元對象都有一個標識符key。當需要元素的時候撼班,先去享元池中通過標識符key進行查找,如果存在的話就直接拿來用,如果不存在的話就直接創(chuàng)建埂软,當元素不用的時候市殷,就把元素放回享元池中,等待復用矮湘,這樣的話就避免每次使用都創(chuàng)建一份元素斟冕,節(jié)省內(nèi)存。
***代理模式:
對象想要做一件事情缅阳,但是他不自己去做宫静,而是設(shè)置其他對象為自己的代理,然后讓其他對象去做。
首先對象需要創(chuàng)建代理協(xié)議孤里,也就是提供一些需要代理對象去做的api接口伏伯,然后引用一個代理對象為成員變量,當對象想要實現(xiàn)某些功能的時候捌袜,調(diào)用代理對象的相應(yīng)的協(xié)議方法说搅。然后代理對象實現(xiàn)協(xié)議方法就可以
4、行為型模式:用于描述類和對象如何交互和如何分配職責
***責任鏈模式(職責鏈模式):
通俗來講就是一層一層的處理事件虏等。比如有一個事件需要處理弄唧,然后有幾個對象都能處理此事件,我們需要把這幾個對象通過引用成員變量的方式串起來霍衫,然后把事件傳給這一系列對象進行處理候引。
應(yīng)用場景:有多個對象都可以處理請求,而處理程序只有在運行時才能確定敦跌,而且不需要關(guān)心是誰處理的澄干,也不用關(guān)心處理過程。
角色:handle處理抽象類柠傍、concretHandle具體處理者
Handle是抽象類:包含Handle類型的成員變量指向下一個nextHandle麸俘,以及一個handleRequest方法([nextHandle handleRequest]),具體實現(xiàn)類重寫handleRequest方法(如果能自己處理就寫處理事件惧笛,否則从媚,調(diào)用[super handleRequest])
游戲中的責任鏈模式應(yīng)用實例
應(yīng)用實例:iOS的響應(yīng)者鏈條。需要知道原理和過程患整。
***命令模式:
命令模式將命令封裝為對象拜效,調(diào)用者通過調(diào)用命令來實現(xiàn)對目標對象的操作,不必要了解目標的任何細節(jié)各谚。因為命令被封裝成了對象紧憾,所以可以把命令當作參數(shù)進行存儲,所以這樣就提供了撤銷和恢復操作嘲碧。
結(jié)構(gòu):
Client:創(chuàng)建命令對象稻励,并設(shè)置接收器
Invoke:命令管理者以及調(diào)用者
Commad:命令的抽象類或者抽象接口
ConcreteCommand:命令的實現(xiàn)類
Receiver:命令的接受者,也是目標者
Invok:excute愈涩、rollBackExcute執(zhí)行和撤銷方法望抽,用來執(zhí)行命令(其實是調(diào)用命令的執(zhí)行,其實是調(diào)用命令的receiver的執(zhí)行)履婉,存儲命令和刪除命令煤篙、撤銷命令(其實是調(diào)用的命令的撤銷,其實是執(zhí)行的命令的receiver的撤銷)
Commad:命令用來執(zhí)行和撤銷毁腿,其實是調(diào)用的receiver的執(zhí)行和撤銷辑奈,需要設(shè)置自身的receiver
Receiver:命令的接收者苛茂,也是目標者,實際用來執(zhí)行和撤銷的對象
***解釋器模式:
給定一個語言鸠窗,定義該語言的文法表示妓羊,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的語句稍计。
優(yōu)點:可以很方便的擴展文法規(guī)則躁绸。
缺點:文法匹配需要大量的循環(huán)和遞歸,執(zhí)行效率比較低臣嚣。會引起類膨脹净刮,因為一個文法至少要定義一個類
***迭代器模式:
本質(zhì)上就是遍歷聚合或者組合對象。
使用場景:需要訪問聚合(組合)對象的內(nèi)容而不暴露其內(nèi)部表示硅则。需要使用多種方式遍歷組合對象淹父。需要提供一個統(tǒng)一的接口,用來遍歷各種類型的組合對象怎虫。
分為內(nèi)部迭代器和外部迭代器暑认。
NSEnumerator外部迭代器
內(nèi)部迭代器
***中介者模式:
定義一個中介者來封裝一系列對象之間的交互,使原有對象之間的耦合松散揪垄,而且還可以動態(tài)改變他們之間的交互穷吮。中介者模式又叫調(diào)停模式逻翁,是迪米特法則的典型應(yīng)用饥努。一個對象應(yīng)該對其他對象保持最少的了解,實現(xiàn)低耦合八回,高內(nèi)聚酷愧。
實際應(yīng)用:MVC模式就是中介者模式
***備忘錄模式:
在不破壞封裝的前提下,捕獲一個對象的內(nèi)部狀態(tài)缠诅,并在該對象之外保存這個狀態(tài)溶浴。這樣以后就可以將該對象恢復到原先保存的狀態(tài)。
備忘錄模式的組成部分:
1管引、原發(fā)器:originator:他是一個普通類士败,可以創(chuàng)建并返回一個存儲他內(nèi)部狀態(tài)的備忘錄,也可以使用備忘錄來恢復其內(nèi)部狀態(tài)褥伴,一般將需要保存內(nèi)部狀態(tài)的類設(shè)計為原發(fā)器谅将。
2、備忘錄:memento:存儲原發(fā)器的內(nèi)部狀態(tài)重慢,根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)饥臂。
3、caretaker:管理者:負責保存?zhèn)渫浰契猓遣粫渫浀膬?nèi)容進行操作或檢查隅熙。
結(jié)構(gòu)圖:
實際應(yīng)用:歸檔解檔稽煤。
需要歸檔解檔的類就是originator原發(fā)器
caretaker(管理者):NSKeyedArchiver archive和unarchive。
備忘錄(Memento)需要遵循NSCoding協(xié)議囚戚,使用的是類NSCoder
-(void)encodeWithCoder:(NSCoder *)encoder {
? ? [encoder encodeObject:self.name forKey:@"name"];
? ? [encoder encodeInt:self.age forKey:@"age"];
? ? [encoder encodeFloat:self.height forKey:@"height"];
}
-(id)initWithCoder:(NSCoder *)decoder {
? ? self.name =[decoder decodeObjectForKey:@"name"];
? ? self.age =[decoder decodeIntForKey:@"age"];
? ? self.height =[decoder decodeFloatForKey:@"height"];
? ? return self;
}
//歸檔到Document文件夾
[NSKeyedArchiver archiveRootObject:person toFile:path];
//解檔類
Person *person =[NSKeyedUnarchiver unarchiveObjectWithFile:path];
// 可以利用歸檔實現(xiàn)深復制酵熙,臨時存儲對象的數(shù)據(jù)
NSData *data =[NSKeyedArchiver archivedDataWithRootObject:person1];
//? 解析data生成一個新的person類
Student *person2 =[NSKeyedUnarchiver unarchiveObjectWithData:data];
***觀察者模式:
觀察者模式定義了一種一對多的組合關(guān)系,以便一個對象的狀態(tài)發(fā)生變化時驰坊,所有依賴于他的對象都得到通知并自動刷新绿店。
應(yīng)用實例:KVO和通知
***狀態(tài)模式:
允許一個對象在其內(nèi)部狀態(tài)改變時修改他的行為,對象看起來似乎修改了他的類庐橙。
該模式用于解決系統(tǒng)中復雜對象的狀態(tài)轉(zhuǎn)化以及不同狀態(tài)下行為的封裝問題假勿。
組成部分:
1、Contex(環(huán)境類):環(huán)境類又稱為上下文态鳖,他是擁有多種狀態(tài)的對象转培。
2、Status(抽象狀態(tài)類):用于定義狀態(tài)處理方法
3浆竭、ConcreteStatus(具體狀態(tài)類):用于實現(xiàn)狀態(tài)處理方法
結(jié)構(gòu)圖
具體狀態(tài)類可通過Context(環(huán)境類)統(tǒng)一實現(xiàn)狀態(tài)的轉(zhuǎn)換
***策略模式:
定義一系列算法浸须,把他們一個個封裝起來,并且使他們可以互相替換邦泄。
角色
環(huán)境類:使用不同策略的類
策略抽象類:定義策略抽象方法
策略具體類:策略抽象方法實現(xiàn)
結(jié)構(gòu)圖:
結(jié)構(gòu)圖跟狀態(tài)模式的很像删窒。
策略模式: 封裝的是一系列的算法,用戶可以自行選擇顺囊。比如出行有地鐵出行肌索,公交出行,我們可以使用調(diào)用出行類的[Go goWIth:subWay];
狀態(tài)模式: 封裝的是內(nèi)部的運行狀態(tài),但是對外而言都是看不見的 [Login login]; login里面自己通過邏輯判斷然后設(shè)置status是哪個類特碳。
***模版方法模式:
就是定義一個父類诚亚,來定義接口規(guī)范,然后不同的行為在子類中實現(xiàn)午乓。
簡單來說就是繼承站宗、重寫、和代碼復用
***訪問者模式:
提供一個作用于某對象結(jié)構(gòu)中的各元素的操作表示益愈,他使我們可以在不改變各元素的類的前提下定義作用于這些元素的新操作梢灭。
角色:
Visit(抽象訪問者):為元素對象結(jié)構(gòu)中每一個具體元素聲明一個訪問操作,這個操作的名稱或者參數(shù)類型可以清楚知道需要訪問的具體元素的類型蒸其,具體訪問者需要實現(xiàn)這些操作方法敏释,定義這些元素的訪問操作。
這些訪問方法的命名一般有兩種方式:一種是直接在方法名中標明待訪問元素對象的具體類型枣接,如visitElementA(ElementA elementA)颂暇,還有一種是統(tǒng)一取名為visit(),通過參數(shù)類型的不同來定義一系列重載的visit()方法但惶。
ConcreteVisitor(具體訪問者):實現(xiàn)了抽象類的用于訪問對象結(jié)構(gòu)中每一個元素的操作耳鸯。
Element(抽象元素):定義一個accept()方法湿蛔,該方法通常以抽象訪問者為參數(shù)。
ConcreteElement(具體元素):具體實現(xiàn)了accept()方法县爬,在方法中調(diào)用訪問者的訪問該具體元素的方法阳啥,完成對該元素的操作。
ObjectStructure(對象結(jié)構(gòu)):對象結(jié)構(gòu)是一個元素的集合财喳,用于存放元素對象察迟,并且提供了遍歷其內(nèi)部元素的方法,他可以結(jié)合組合模式來實現(xiàn)耳高,也可以是一個簡單的集合對象扎瓶,比如一個List對象。
結(jié)構(gòu)圖:
代碼示例:
優(yōu)點:增加新的訪問者很簡單泌枪,可以對元素進行不同的操作概荷。
缺點:增加新的元素比較麻煩,需要訪問者定義訪問該元素的方法碌燕,并且每個具體訪問者都需要實現(xiàn)該方法误证。