iOS設(shè)計(jì)模式
前言
可能我們?cè)诤芏嗑帉?xiě)代碼的時(shí)候都沒(méi)有留意,其實(shí)設(shè)計(jì)模式無(wú)處不在,iOS中經(jīng)典的模式我們都耳熟能詳,例如單例模式,工廠模式等等,設(shè)計(jì)模式可以?xún)?yōu)化框架的維護(hù)性,也可以使得程序在迭代的過(guò)程中更便于擴(kuò)展,設(shè)計(jì)模式聽(tīng)上去距離我們很遠(yuǎn).
但事實(shí)上卻距離我們很近,在最初學(xué)習(xí)iOS的過(guò)程中,并沒(méi)有什么需要擴(kuò)展程序的概念,但是在后期加入工作當(dāng)中的時(shí)候才明白,很多需要并不是一下子就能確定下來(lái)的,而是在交流和溝通的過(guò)程中不斷修改的.而我們?cè)趯W(xué)習(xí)編程的過(guò)程中,為了避免重復(fù)編碼,復(fù)制黏貼這樣的毛病,在設(shè)計(jì)界面,理念,邏輯的時(shí)候要考慮好程序的健壯性,思考才是提升能力的關(guān)鍵,既然能打開(kāi)網(wǎng)絡(luò)的世界,瀏覽不同的最新資訊,想必大家也是非常好學(xué)和樂(lè)意提高自己的編程水平的.
寫(xiě)下這篇文章主要是記錄下自己學(xué)習(xí)過(guò)程中的一些心得吧,之前看到設(shè)計(jì)模式龐大的家族有點(diǎn)被震住,感覺(jué)并不是每種設(shè)計(jì)模式都要生搬硬套到自己的項(xiàng)目當(dāng)中,判斷設(shè)計(jì)模式對(duì)當(dāng)前系統(tǒng)是否有幫助才是最重要的,每種設(shè)計(jì)模式都有自己的優(yōu)點(diǎn)和不足之處,所以在編程的過(guò)程中還請(qǐng)大家更多的思考.
本人在學(xué)習(xí)的過(guò)程中主要是參考了 [大話設(shè)計(jì)模式],雖然語(yǔ)言是Java寫(xiě)的,但是在閱讀的過(guò)程中也有很多收獲,Java作為一門(mén)古老而健壯的語(yǔ)言已經(jīng)很很長(zhǎng)歷史了,所以設(shè)計(jì)模式在Java中可能更為體系,而iOS中我看過(guò)一本名為[Objective-C編程之道:IOS設(shè)計(jì)模式解析],感覺(jué)里面的內(nèi)容更為切合與貼近iOS的程序開(kāi)發(fā),感謝作者的總結(jié)和分享,我也希望大家能親自去閱讀這兩本書(shū),能夠直接在里面獲得更深刻的體會(huì)和認(rèn)識(shí).
我認(rèn)為好書(shū)要多讀幾遍才能深刻理解,常翻常新,這里是本人的初讀體會(huì),但隨著時(shí)間的推移,也會(huì)及時(shí)更新本人對(duì)設(shè)計(jì)模式的理解和感悟,謝謝
一. 簡(jiǎn)單工廠模式
1.定義:
簡(jiǎn)單工廠模式(Simple Factory Pattern)屬于類(lèi)的創(chuàng)新型模式,又叫靜態(tài)工廠方法模式(Static FactoryMethod Pattern),是通過(guò)專(zhuān)門(mén)定義一個(gè)類(lèi)來(lái)負(fù)責(zé)創(chuàng)建其他類(lèi)的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類(lèi)。
工廠模式大家都非常熟悉,也可以寫(xiě)出結(jié)構(gòu)優(yōu)良的工廠模式代碼,所以這里就簡(jiǎn)單的舉例說(shuō)明即可.
2.模式:
假設(shè)現(xiàn)在有一家工廠在生產(chǎn)手機(jī),但是手機(jī)的類(lèi)型暫時(shí)還不確定,等到用戶(hù)需求確定了,再?zèng)Q定擴(kuò)大某條生產(chǎn)線來(lái)生產(chǎn)手機(jī),該工廠主要有兩種產(chǎn)品:Android和IPhone;
SMMobilePhone.h
- 主要就是一個(gè)useCall方法.
#import <Foundation/Foundation.h>
//手機(jī)基類(lèi)
@interface SMMobilePhone : NSObject
-(void)useCall;
@end
//蘋(píng)果,繼承手機(jī)基類(lèi)
@interface SMIPhone : SMMobilePhone
@end
//安卓,繼承手機(jī)基類(lèi)
@interface SMAndroid : SMMobilePhone
@end
SMMobilePhone.m
- 類(lèi)的初始化和方法的實(shí)現(xiàn),沒(méi)啥好解析的
#import "SMMobilePhone.h"
@implementation SMMobilePhone
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"%@類(lèi)初始化中",self.class);
}
return self;
}
-(void)useCall{
NSLog(@"使用%@,呼叫10086中...",self.class);
}
@end
@implementation SMIPhone
@end
@implementation SMAndroid
@end
接下來(lái)為了使用用戶(hù)的不同需求,我們對(duì)建立了一個(gè)工廠模式,用戶(hù)需要什么類(lèi)型的手機(jī),只有下單了,我們都可以直接生產(chǎn)成型:
SMFactoryImpl.h
- 提供類(lèi)方法返回手機(jī)
#import <Foundation/Foundation.h>
#import "SMMobilePhone.h"
//選擇生產(chǎn)什么手機(jī)
typedef enum : NSInteger{
IPhone,
Android,
}IPhoneType;
@protocol SMFactory <NSObject>
//提供類(lèi)方法直接返回手機(jī)
+(SMMobilePhone*)creatMobileWithType:(IPhoneType)type;
@end
@interface SMFactoryImpl : NSObject<SMFactory>
@end
SMFactoryImpl.m
- 利用枚舉判斷手機(jī)類(lèi)型,實(shí)例化手機(jī)類(lèi)(此處可以繼續(xù)添加不同種類(lèi)的手機(jī))
- 利用了類(lèi)的多態(tài)形式,返回值是父類(lèi),工廠生產(chǎn)出來(lái)的是子類(lèi)
#import "SMFactoryImpl.h"
@implementation SMFactoryImpl
+(SMMobilePhone *)creatMobileWithType:(IPhoneType)type{
switch (type) {
case IPhone:
return [[SMIPhone alloc]init];
break;
case Android:
return [[SMAndroid alloc]init];
break;
}
}
@end
Test.m
- 代碼測(cè)試
SMMobilePhone * iphone = [SMFactoryImpl creatFactoryWithType:IPhone];
SMMobilePhone * android = [SMFactoryImpl creatFactoryWithType:Android];
[iphone useCall];
[android useCall];
//打印結(jié)果
//Design4OC[3636:398753] SMIPhone類(lèi)初始化中
//Design4OC[3636:398753] SMAndroid類(lèi)初始化中
//Design4OC[3636:398753] 使用SMIPhone,呼叫10086中...
//Design4OC[3636:398753] 使用SMAndroid,呼叫10086中...
3. 用途:
工廠模式的用途非常廣泛,可以基于面向?qū)ο蟮娜筇匦赃_(dá)到不同的效果,比如復(fù)雜類(lèi)的構(gòu)建過(guò)程(封裝),父類(lèi)指針可以指向子類(lèi)對(duì)象(多態(tài)),也可以減低業(yè)務(wù)之間的耦合度.
二.策略模式
1.定義:
策略模式(Strategy) : 它定義了算法家族,分別封裝起來(lái),讓它們之間可以相互替換,此模式讓算法的改變,不會(huì)影響到算法的客戶(hù)端.
用通俗一點(diǎn)的話來(lái)說(shuō),就是更改算法,不需要大量改動(dòng)客戶(hù)端內(nèi)容.
2.模式:
比如現(xiàn)在商場(chǎng)是按原價(jià)將貨物售出,但是遇到周年慶,老板打算讓所有商品都按9.5折出售,這個(gè)業(yè)務(wù)很簡(jiǎn)單,只需要在代碼上乘以一個(gè)系數(shù)就可以了(此時(shí)已經(jīng)修改了客戶(hù)端源碼,有不妥之處).
過(guò)了一段時(shí)間以后,老板大婚,決定讓商場(chǎng)里面的所有貨物都打8.0出售,此時(shí)是不是我們又要去修改代碼才能改變折扣呢?
...為了將臨近過(guò)期的貨物進(jìn)口售出,此時(shí)老板決定臨近過(guò)期的商品3.0折出售,另外,如果客戶(hù)買(mǎi)滿(mǎn)300減免100元.在業(yè)務(wù)不斷變化的情況下,如果要經(jīng)常修改源碼才能滿(mǎn)足業(yè)務(wù)需求的話,那可能要思考一下是否要改變一下代碼的構(gòu)造.
我們引入一個(gè)更為簡(jiǎn)單的例子來(lái)引入策略模式.
我們熟悉的鋼鐵俠既能在陸地急速奔跑,也能在水中潛行,更不要說(shuō)在天空自由飛翔了,那么,每次切換不同模式,我們都可以理解為有一套最優(yōu)的策略(算法)來(lái)適應(yīng)陸地,海水和天空.
我們先定義個(gè)接口類(lèi)來(lái)統(tǒng)一方法的調(diào)用:
IStrategy.h
- operate含有相關(guān)操作
#import <Foundation/Foundation.h>
@protocol IStrategy <NSObject>
-(void)operate;
@end
STContext.h
- 上下文,用來(lái)對(duì)策略(算法)的引用,使用初始化方法initWithIstragty賦值id<IStrategy>引用
#import <Foundation/Foundation.h>
#import "IStrategy.h"
@interface STContext : NSObject<IStrategy>
+(instancetype)initWithIstragty:(id<IStrategy>)iStrategy;
@end
STContext.m
- 此處只要實(shí)現(xiàn)了算法接口的類(lèi)都可以當(dāng)做實(shí)現(xiàn)類(lèi)id傳入,內(nèi)部獲取到id的引用以后,直接調(diào)用同名方法operate,內(nèi)部再調(diào)用id的operate方法,直到現(xiàn)在,STContext不需要管id的實(shí)現(xiàn),主要對(duì)接口調(diào)用即可.(可以慢慢體會(huì)面向接口編程的好處.)
#import "STContext.h"
@interface STContext()
@property (nonatomic , strong ) STContext *impl;
@end
@implementation STContext
+(instancetype)initWithIstragty:(id<IStrategy>)iStrategy{
STContext * i = [[STContext alloc] init];
i.impl = iStrategy;
return i;
}
-(void)operate{
[self.impl operate];
}
@end
然后有三個(gè)實(shí)現(xiàn)了IStrategy接口的類(lèi),分別是:STFly,STSwimming,STSwimming
#import "IStrategy.h"
@interface STFly : NSObject<IStrategy>
@end
--------------------
#import "IStrategy.h"
@interface STSwimming : NSObject<IStrategy>
@end
--------------------
#import "IStrategy.h"
@interface STRunning : NSObject<IStrategy>
@end
.m
- 此處是各個(gè).m文件對(duì)接口的實(shí)現(xiàn),對(duì)號(hào)入座即可.
-(void)operate{
NSLog(@"啟動(dòng)飛行模式");//fly
NSLog(@"啟動(dòng)游泳模式");//swimming
NSLog(@"啟動(dòng)奔跑模式");//running
}
Test.m
- 此時(shí)你可以發(fā)現(xiàn),主要簡(jiǎn)單的修改類(lèi),即可完全改變算法,切換成奔跑模式,飛行模式,潛行模式等
- 再如一開(kāi)始商場(chǎng)的例子,雖然可能會(huì)寫(xiě)多一些類(lèi),但是主要給UI加上一個(gè)選擇器變可以直接改變定價(jià)策略,修改價(jià)格和優(yōu)惠
//此處的STFly可以替換成Swimming或者Running
STContext * is = [STContext initWithIstragty:[[STFly alloc]init]];
NSLog(@"%@",is);
[is operate];
3. 用途:
- 策略模式可以用來(lái)定義一系列是算法,對(duì)外提供的接口是相同的,只是改變了內(nèi)部的實(shí)現(xiàn)即產(chǎn)生了不同的效果,核心作用就是用于封裝算法
- 原本我們可以將所有操作封裝在一個(gè)類(lèi)里面,每次增加策略就需要在里面增加
if else或者switch語(yǔ)句,但是你會(huì)發(fā)現(xiàn)這樣耦合度很高,不方便單個(gè)算法的測(cè)試--從而也引出了策略模式的另外一個(gè)好處:簡(jiǎn)單化了單元測(cè)試,每一個(gè)算法就是一個(gè) - 個(gè)人感覺(jué)這里算是一個(gè)面向?qū)ο筇匦缘囊粋€(gè)思想提升--運(yùn)用了協(xié)議來(lái)統(tǒng)一工作方式,實(shí)質(zhì)上這三個(gè)類(lèi)是三個(gè)獨(dú)立的存在的類(lèi),并沒(méi)有規(guī)定統(tǒng)一父類(lèi),他們都有著自己的模式和算法.但我的思想暫時(shí)還沒(méi)提高到策略模式的算法層面,只能暫時(shí)理解為解決問(wèn)題的一種策略模式,可能還有偏頗!
三.裝飾模式
1.定義:
裝飾模式(Decorate),動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來(lái)說(shuō),裝飾模式比生成之類(lèi)更為靈活
2.模式:
如何理解裝飾呢?比如說(shuō)人身上的衣服,鞋子,褲子,帽子都是人身上裝飾物品,沒(méi)有裝飾物的人依然有著能自由活動(dòng),吃飯,睡覺(jué)的基礎(chǔ)功能.
再比如我們生活中常見(jiàn)的汽車(chē),我們知道有低配版和高配版之分,只要價(jià)格給上去了,很多額外的裝飾物都可以給你添上比如普通大燈換為疝氣燈,普通沙發(fā)換成真皮沙發(fā)等等...首先可以理解為為某個(gè)類(lèi)增加功能(增加特殊功能但不改變?cè)械念?lèi))
我們舉一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明一下,
首先我們有個(gè)組裝的協(xié)議
DEComponent.h
- 該協(xié)議里面含有一個(gè)operation的基礎(chǔ)操作方法,類(lèi)比汽車(chē)的時(shí)候可以理解為:即使我沒(méi)有真皮沙發(fā),沒(méi)有疝氣大燈,依然可以執(zhí)行最基礎(chǔ)的行駛功能
#import <Foundation/Foundation.h>
@protocol DEComponent <NSObject>
-(void)operation;
@end
DEBaseCar.h
- 然后,我們以車(chē)為例子,當(dāng)然需要有一個(gè)車(chē)的實(shí)例,該類(lèi)遵守了協(xié)議,里面有operation方法
- baseCar獨(dú)立存在,可以不知道有其他裝飾類(lèi)的存在,不影響最基礎(chǔ)的工作
#import <Foundation/Foundation.h>
#import "DEComponent.h"
@interface DEBaseCar : NSObject<DEComponent>
@end
DEBaseCar.m
#import "DEBaseCar.h"
@implementation DEBaseCar
-(void)operation{
NSLog(@"最基本的車(chē),具備了安全行駛功能");
}
@end
以上功能,我們就具備了一輛最基礎(chǔ)的車(chē),但是現(xiàn)在這里車(chē)沒(méi)有滿(mǎn)足你的要求,你想要加裝一些零部件,這個(gè)時(shí)候就有了新的需求
DECarComponent.h
- DECarDecorator是裝飾者類(lèi),它定義了extraComponentOn:方法用來(lái)增強(qiáng)baseCar的功能,也對(duì)對(duì)象進(jìn)行了包裝,從而實(shí)現(xiàn)了每個(gè)裝飾者類(lèi)都可以有自己獨(dú)特的功能(這樣的結(jié)構(gòu)會(huì)使得功能相互獨(dú)立開(kāi)來(lái),有了很好的擴(kuò)展),并在有需要的時(shí)候增強(qiáng)baseCar功能實(shí)現(xiàn)
- DECarDecorator它可以是抽象基類(lèi),也可以是單個(gè)裝飾者類(lèi)
- DESofaDecorator,DELightDecorator,DEDogDecorator作為裝飾者類(lèi)的子類(lèi),都有著自己獨(dú)特的方法和屬性
- DESofaDecorator作為普通類(lèi),只給車(chē)輛換上沙發(fā)
- DELightDecorator給汽車(chē)賦予了新亮度,有遠(yuǎn)光和近光的功能實(shí)現(xiàn)(裝飾后擁有的獨(dú)特方法)
- DEDogDecorator香車(chē)洋狗和美女都是配在一起的,而狗可以有自己的名字(其他類(lèi)不會(huì)有狗,狗也不會(huì)有名字)
#import <Foundation/Foundation.h>
#import "DEComponent.h"
@interface DECarDecorator : NSObject<DEComponent>
@property (nonatomic , strong ,setter=extraComponentOn:) id<DEComponent> component;
@end
/*------------------作為裝飾汽車(chē)的沙發(fā)(不含參數(shù)和方法)--------------------*/
@interface DESofaDecorator : DECarDecorator
@end
/*------------------作為裝飾汽車(chē)的疝氣燈(含私有方法)--------------------*/
@interface DELightDecorator : DECarDecorator
@end
/*------------------作為裝飾汽車(chē)的養(yǎng)狗(含公有屬性)--------------------*/
@interface DEDogDecorator : DECarDecorator
@property (nonatomic , strong ) NSString *name;
@end
DECarComponent.m
- 類(lèi)的屬性和方法已經(jīng)在上面簡(jiǎn)要說(shuō)明,可以忽略這部分不看.
#import "DECarDecorator.h"
@implementation DECarDecorator
-(void)extraComponentOn:(id<DEComponent>)component{
_component = component;
}
-(void)operation{
if (self.component != nil) {
[self.component operation];
}
}
@end
/*------------------作為裝飾汽車(chē)的沙發(fā)(不含參數(shù)和方法)--------------------*/
@implementation DESofaDecorator
-(void)operation{
[super operation];
NSLog(@"高配版要的就是豪華沙發(fā)...");
}
@end
/*------------------作為裝飾汽車(chē)的沙發(fā)(含私有方法)--------------------*/
//遠(yuǎn)光近光
typedef enum : NSUInteger {
HighLight,
LowLitght
} LightType;
@implementation DELightDecorator
-(void)operation{
[super operation];
NSLog(@"裝飾了疝氣燈以后可以開(kāi)啟照明功能");
[self openLight:arc4random()%2];
}
-(void)openLight:(LightType)ligthType{
switch (ligthType) {
case HighLight:
NSLog(@"開(kāi)啟了遠(yuǎn)光燈");
break;
case LowLitght:
NSLog(@"開(kāi)啟了近光燈");
break;
}
}
@end
/*------------------作為裝飾汽車(chē)的養(yǎng)狗(含公有屬性)--------------------*/
@implementation DEDogDecorator
-(void)operation{
[super operation];
if (self.name ==nil) {
NSLog(@"剛剛撿回來(lái)的小狗沒(méi)有名字");
}else{
NSLog(@"%@,跟大家打招呼吧!",self.name);
}
}
@end
Test.m
NSLog(@"---最新出廠---");
DEBaseCar * car = [[DEBaseCar alloc] init];
NSLog(@"---把沙發(fā),疝氣燈,狗搬到裝配車(chē)間---");
DESofaDecorator * sofaDecorator = [[DESofaDecorator alloc] init];
DELightDecorator * lightDecorator = [[DELightDecorator alloc] init];
DEDogDecorator * dogDecorator = [[DEDogDecorator alloc] init];
//設(shè)置狗的名字
[dogDecorator setName:@"旺財(cái)"];
NSLog(@"開(kāi)始裝飾,將需要的東西組裝到車(chē)上");
[sofaDecorator extraComponentOn:car];
[lightDecorator extraComponentOn:sofaDecorator];
[dogDecorator extraComponentOn:lightDecorator];
NSLog(@"---------------------");
[dogDecorator operation];
//打印結(jié)果
//Design4OC[8316:763030] ---最新出廠---
//Design4OC[8316:763030] ---把沙發(fā),疝氣燈,狗搬到裝配車(chē)間---
//Design4OC[8316:763030] 開(kāi)始裝飾,將需要的東西組裝到車(chē)上
//Design4OC[8316:763030] ---------------------
//Design4OC[8316:763030] 最基本的車(chē),具備了安全行駛功能
//Design4OC[8316:763030] 高配版要的就是豪華沙發(fā)...
//Design4OC[8316:763030] 裝飾了疝氣燈以后可以開(kāi)啟照明功能
//Design4OC[8316:763030] 開(kāi)啟了近光燈
3. 用途:
- 裝飾者模式可以理解為在一個(gè)基礎(chǔ)的類(lèi)上,增強(qiáng)或增加某些功能的實(shí)現(xiàn),大家在維護(hù)老代碼的時(shí)候不一定方便直接修改源碼(當(dāng)增強(qiáng)的功能并不常用,這個(gè)時(shí)候其實(shí)不應(yīng)該直接修改源碼,額外的增加了這個(gè)類(lèi)的職責(zé)和負(fù)擔(dān),違背了職責(zé)單一的原則)
- 把核心功能和其他功能區(qū)分開(kāi),方便拆裝和管理
- 個(gè)人感覺(jué),使用分類(lèi)也能增加類(lèi)的實(shí)現(xiàn),在不導(dǎo)入分類(lèi)的頭文件時(shí),其實(shí)類(lèi)的功能也沒(méi)有加載進(jìn)來(lái),也像是一種裝飾者的特殊實(shí)現(xiàn)吧,以后有更多的體會(huì)再來(lái)更改現(xiàn)在的想法
四.工廠方法模式
1.定義
工廠方法模式(Factory Method):定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類(lèi)決定實(shí)例化哪一個(gè)類(lèi).工廠方法使得一個(gè)類(lèi)的實(shí)例化延遲到其子類(lèi).
2. 模式
為何前面有說(shuō)一個(gè)簡(jiǎn)單工廠模式,現(xiàn)在又來(lái)一個(gè)工廠方法模式?看到定義估計(jì)還比較模模糊糊,實(shí)際上工廠模式是簡(jiǎn)單工廠模式的抽象和推廣.
簡(jiǎn)單工廠模式是在內(nèi)部case判斷返回什么內(nèi)容給控制器,同一個(gè)工廠可以生產(chǎn)不同的產(chǎn)品,而如果需要修改產(chǎn)品的類(lèi)型,就需要重新在內(nèi)部部署生產(chǎn)設(shè)備,在工廠內(nèi)部協(xié)調(diào)工作
而工廠模式是在控制器就選擇好了要生成什么類(lèi)型的類(lèi),不同的工廠生產(chǎn)不同的產(chǎn)品,每個(gè)工廠都有自己獨(dú)特的產(chǎn)品,接到指令時(shí)不需要在內(nèi)部協(xié)調(diào)就可以直接生產(chǎn)產(chǎn)品.先上代碼:
FMMobilePhoneFactory.h
- FMFactoryProtocol: 是工廠模式的抽象接口,使子類(lèi)有一個(gè)實(shí)例化方法
- FMMobilePhoneFactory:作為工廠基類(lèi),其他的工廠類(lèi)應(yīng)該繼承該父類(lèi)
- FMIPhoneFactory和FMAndroidFactory: 父類(lèi)是FMMobilePhoneFactory,使用協(xié)議方法的時(shí)候可以實(shí)例化具體的產(chǎn)品
#import <Foundation/Foundation.h>
#import "FMMobilePhone.h"
@class FMMobilePhoneFactory;
//
@protocol FMFactoryProtocol<NSObject>
-(FMMobilePhone*)creatFactory;
@end
//工廠基類(lèi)
@interface FMMobilePhoneFactory : NSObject<FMFactoryProtocol>
@end
//蘋(píng)果工廠,繼承工廠基類(lèi)
@interface FMIPhoneFactory : FMMobilePhoneFactory
@end
//安卓工廠,繼承工廠基類(lèi)
@interface FMAndroidFactory : FMMobilePhoneFactory
@end
FMMobilePhoneFactory.m
- 不同的工廠生產(chǎn)不同的產(chǎn)品,基類(lèi)不可直接使用,這里不再做判斷
#import "FMMobilePhoneFactory.h"
#import "FMMobilePhone.h"
@implementation FMMobilePhoneFactory
-(FMMobilePhone*)creatFactory{
return [[FMMobilePhone alloc]init];
}
@end
@implementation FMIPhoneFactory
-(FMMobilePhone *)creatFactory{
return [[FMIPhone alloc]init];
}
@end
@implementation FMAndroidFactory
-(FMMobilePhone *)creatFactory{
return [[FMAndroid alloc]init];
}
@end
FMMobilePhone.h
FMMobilePhone.m
- 產(chǎn)品類(lèi)在簡(jiǎn)單工廠模式的時(shí)候已經(jīng)講過(guò)了,也比較簡(jiǎn)單,自己可以看看
#import <Foundation/Foundation.h>
@interface FMMobilePhone : NSObject
-(void)useCall;
@end
//蘋(píng)果,繼承手機(jī)基類(lèi)
@interface FMIPhone : FMMobilePhone
@end
//安卓,繼承手機(jī)基類(lèi)
@interface FMAndroid : FMMobilePhone
@end
//-------------.m------------------
#import "FMMobilePhone.h"
@implementation FMMobilePhone
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"%@類(lèi)初始化中",self.class);
}
return self;
}
-(void)useCall{
NSLog(@"使用%@,呼叫10086中...",self.class);
}
@end
@implementation FMIPhone@end
@implementation FMAndroid@end
Test.m
- 下面是控制器的調(diào)用,我們可以看到,其實(shí)兩臺(tái)代碼完全相同,就只是在實(shí)例化工廠的時(shí)候選擇了FMIPhoneFactory和FMAndroidFactory就可以改變產(chǎn)品的產(chǎn)品,避免了產(chǎn)品增多的時(shí)候要修改工廠類(lèi)(實(shí)際上是違背了封閉-開(kāi)放原則)
FMMobilePhoneFactory * iphoneFactory = [[FMIPhoneFactory alloc]init];
FMMobilePhone * iphone = [iphoneFactory creatFactory];
[iphone useCall];
FMMobilePhoneFactory * andoridFactory = [[FMAndroidFactory alloc]init];
FMMobilePhone * andorid = [andoridFactory creatFactory];
[andorid useCall];
//FMIPhone類(lèi)初始化中
//使用FMIPhone,呼叫10086中...
//FMAndroid類(lèi)初始化中
//使用FMAndroid,呼叫10086中...
3.用途
- 實(shí)際上是簡(jiǎn)單工廠模式的抽象和推廣,克服了簡(jiǎn)單工廠模式違背了封閉-開(kāi)放原則,又保持了封裝對(duì)象的優(yōu)點(diǎn)
- 把從工廠的內(nèi)部判斷移動(dòng)到了控制器,產(chǎn)生子類(lèi)的選擇更加靈活
- 個(gè)人感覺(jué),一直都習(xí)慣了在工廠類(lèi)中做了條件判斷來(lái)生產(chǎn)不同的產(chǎn)品類(lèi),而且每產(chǎn)生多一個(gè)產(chǎn)品就要多一個(gè)工廠類(lèi)和產(chǎn)品類(lèi),工作量有所增加,如果能用運(yùn)行時(shí)來(lái)產(chǎn)生不同的子類(lèi)更好
五.原型模式
1.定義
原型模式(Prototype):用原型實(shí)例指定創(chuàng)建對(duì)象的種類(lèi),并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象.
2.模式
其實(shí)這篇文章就是要說(shuō)明復(fù)制這個(gè)概念,網(wǎng)上也很多文章無(wú)數(shù)次分析深拷貝和淺拷貝了,這里就不在詳細(xì)說(shuō)明深拷貝和淺拷貝的內(nèi)容了,深拷貝復(fù)制對(duì)象,淺拷貝復(fù)制引用地址.
現(xiàn)在來(lái)舉例說(shuō)明,我們有一個(gè)學(xué)生對(duì)象和書(shū)本對(duì)象,里面分別有如下屬性:
PTStudent.h
- 學(xué)生手中有一本書(shū),直接拿在手上看的,屬性為:book
- 書(shū)包里面有很多書(shū),用
NSMutableArray<PTBook*>存儲(chǔ) - 學(xué)生還有名字和年齡兩個(gè)屬性
- 學(xué)生還有不同的老師,用字符串來(lái)表示
NSMutableArray<NSString*>(也可以理解為老師只有一個(gè),大家共享這個(gè)字符串的資源)
PTStudent.h
#import <Foundation/Foundation.h>
#import "PTBook.h"
@interface PTStudent : NSObject<NSCopying>
@property (nonatomic , strong ) PTBook *book;
@property (nonatomic , strong ) NSString *name;
@property (nonatomic , assign ) int age;
@property (nonatomic , strong ) NSMutableArray<NSString*> *teachers;
@property (nonatomic , strong ) NSMutableArray<PTBook*> *package;
@end
PTBook.h
- 書(shū)本有名字
#import <Foundation/Foundation.h>
@interface PTBook : NSObject<NSCopying>
@property (nonatomic , strong ) NSString *bookName;
@end
PTBook.m
-
book.bookName = [NSString stringWithFormat:@"%@",self.bookName];這個(gè)是作為深復(fù)制存在,開(kāi)辟了新的內(nèi)存空間 -
book.bookName = [self.bookName copy];此時(shí)使用的是淺復(fù)制,共享常量字符串的內(nèi)容,可以結(jié)合內(nèi)存地址查看
#import "PTBook.h"
@implementation PTBook
-(id)copyWithZone:(NSZone *)zone{
book.bookName = [NSString stringWithFormat:@"%@",self.bookName];
/*
(lldb) p student.book.bookName
(__NSCFConstantString *) $0 = 0x00000001043616b0 @"B"
(lldb) p otherStudent.book.bookName
(NSTaggedPointerString *) $1 = 0xa000000000000421 @"B"
(lldb)
*/
//book.bookName = [self.bookName copy];
/*
(lldb) p student.book.bookName
(__NSCFConstantString *) $0 = 0x00000001074466b0 @"B"
(lldb) p otherStudent.book.bookName
(__NSCFConstantString *) $1 = 0x00000001074466b0 @"B"
(lldb)
*/
}
@end
PTStudent.m
- 常量字符串在常量區(qū)共享,也可以用字符串對(duì)象開(kāi)辟新的內(nèi)存空間
- 由于age是NSUInteger類(lèi)型的,屬于基本數(shù)據(jù)類(lèi)型,則直接賦值
- book對(duì)象遵守了NSCopying協(xié)議,里面已經(jīng)處理,可以直接用copy方法深復(fù)制
- teachers作為了常量字符串容器,里面的字符串是共享的,但是容器是作了深拷貝
- package里面裝有book對(duì)象,除了拿到新容器外,還需要把book存進(jìn)容器里面,這個(gè)時(shí)候就可以使用遍歷或者
copyItems:YES這種方式(后來(lái)才找到有這個(gè)方法...)
#import "PTStudent.h"
@interface PTStudent()
@end
@implementation PTStudent
- (instancetype)init
{
self = [super init];
if (self) {
PTBook* book = [[PTBook alloc]init];
book.bookName = @"新華字典";
PTBook* bookA = [[PTBook alloc]init];
book.bookName = @"A";
PTBook* bookB = [[PTBook alloc]init];
book.bookName = @"B";
self.book = book;
self.name = @"小明";
self.age = 18;
self.teachers = [NSMutableArray arrayWithObjects:@"張飛",@"劉備",@"關(guān)羽", nil];
self.package=[NSMutableArray arrayWithObjects:bookA,bookB, nil];
}
return self;
}
-(id)copyWithZone:(NSZone *)zone{
PTStudent* student = [[[self class] allocWithZone:zone]init];
student.age = self.age;
student.book = [self.book copy];
//這里會(huì)深復(fù)制,再開(kāi)辟內(nèi)存空間
student.name = [NSString stringWithFormat:@"%@",self.name];
student.teachers = [NSMutableArray arrayWithArray:self.teachers];
//NSMutableArray<PTBook*> *package;
student.package = [NSMutableArray array];
//原本是打算通過(guò)遍歷的方法來(lái)深復(fù)制
// [self.package enumerateObjectsUsingBlock:^(PTBook * _Nonnull book, NSUInteger idx, BOOL * _Nonnull stop) {
// [student.package addObject:[book copy]];
// }];
//后來(lái)發(fā)現(xiàn)一種不需要遍歷的方法,如果你只需要數(shù)組中數(shù)組第一層的深拷貝,那么
student.package= [[NSMutableArray alloc] initWithArray:self.package copyItems:YES];
//這里是淺復(fù)制
//指向的是常量區(qū)
////student.name = [self.name copy];
//此處數(shù)組可以復(fù)制,但是里面每一個(gè)元素的指針都是沒(méi)有被復(fù)制的,和原來(lái)的類(lèi)型指向同一內(nèi)容
//student.teachers = [self.teachers mutableCopy];
// student.package = [NSMutableArray mutableCopy];
return student;
}
@end
3.用途
- 需要重復(fù)創(chuàng)建對(duì)象的時(shí)候可以考慮使用原型模式
- 原型模式也屬于創(chuàng)建模式的一種,工廠模式會(huì)new一個(gè)全新的對(duì)象,使用原型模式就可以拷貝一份對(duì)象出來(lái),工廠模式和原型模式其實(shí)效率差不多,但是如果在大量創(chuàng)建數(shù)據(jù)的時(shí)候,原型模式則更加節(jié)省資源,效率更高.
六.命令模式
1.定義
命令模式(Command):將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,從而使得你可以用不同的請(qǐng)求對(duì)客戶(hù)進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志,以及支持可撤銷(xiāo)的操作
命令模式把動(dòng)作對(duì)象和指揮對(duì)象拆分開(kāi)來(lái),即是有人能夠安排事情的進(jìn)展,設(shè)計(jì)出邏輯優(yōu)越的命令隊(duì)列,用生活的事情作比喻就是領(lǐng)導(dǎo)安排誰(shuí)先去做某件事情,接下來(lái)做什么,最后做什么.動(dòng)作由下屬去操作,而命令由領(lǐng)導(dǎo)來(lái)發(fā)出
2.模式
下面用一個(gè)例子來(lái)說(shuō)明命令模式的含義,主要有三個(gè)類(lèi),一個(gè)類(lèi)是將軍類(lèi),一個(gè)是命令類(lèi),一個(gè)是軍艦類(lèi),將軍通過(guò)安排不同的命令,讓海軍去執(zhí)行攻擊與防御的操作
CDWarship.h
- 軍艦類(lèi),具有攻擊和防御功能
#import <Foundation/Foundation.h>
@interface CDWarship : NSObject
-(void)attackShip;
-(void)attackAirplane;
-(void)defense;
@end
CDCommand.h&CommandProtocol
- CommandProtocol統(tǒng)一了命令的接口
-
CDCommand作為抽象類(lèi),子類(lèi)必須實(shí)現(xiàn)接口中的
excuteCommand方法 - CDAttackShipCommand,CDAttackAirplaneCommand,CDDefenseCommand作為子類(lèi)重寫(xiě)了父類(lèi)的方法,即雖然調(diào)用的方法名相同,但都有自己獨(dú)特的實(shí)現(xiàn)方法,比如攻擊飛機(jī),攻擊潛艇,進(jìn)行防御
- initWithWarship:指明是哪一個(gè)潛艇來(lái)受理命令,也可以獨(dú)立再寫(xiě)一個(gè)方法orderToWarship:配置受理命令的船,這里忽略吧~
#import <Foundation/Foundation.h>
#import "CDWarship.h"
//該協(xié)議用來(lái)聲明操作的接口
@protocol CommandProtocol<NSObject>
-(void)excuteCommand;
@end
@interface CDCommand : NSObject<CommandProtocol>
//命令下達(dá)給某軍艦,一切攻擊由它發(fā)起
-(instancetype)initWithWarship:(CDWarship*)warship;
@end
//攻擊潛艇命令
@interface CDAttackShipCommand : CDCommand @end
//攻擊飛機(jī)命令
@interface CDAttackAirplaneCommand : CDCommand @end
@interface CDDefenseCommand : CDCommand @end
CDCommand.m
- 這里是類(lèi)的實(shí)現(xiàn),沒(méi)有什么特別的地方,需要說(shuō)明的是,為了將command做成抽象基類(lèi),在初始化方法里面做了判斷,如果不是子類(lèi)則斷言生效,提示要重寫(xiě)
- 抽象基類(lèi)的接口方法用了異常拋出,強(qiáng)制子類(lèi)重寫(xiě)該方法的實(shí)現(xiàn)
#import "CDCommand.h"
#import "Common.h"
@interface CDCommand()
@property (nonatomic , strong ) CDWarship *warship;
@end
@implementation CDCommand
-(instancetype)init{
NSAssert(![self isMemberOfClass:[CDCommand class]], @"CDCommand is an abstract class, you should not instantiate it directly.");
return self;
}
-(instancetype)initWithWarship:(CDWarship *)warship{
self = [self init];
if (self) {
self.warship = warship;
}
return [self init];
}
-(void)excuteCommand{
MASMethodNotImplemented();
}
@end
//------------攻擊潛艇命令
@implementation CDAttackShipCommand
-(void)excuteCommand{
[self.warship attackShip];
}
@end
//-------------攻擊飛機(jī)命令
@implementation CDAttackAirplaneCommand
-(void)excuteCommand{
[self.warship attackAirplane];
}
@end
//防御命令
@implementation CDDefenseCommand
-(void)excuteCommand{
[self.warship defense];
}
@end
CDMasters.h
司令官,陸軍統(tǒng)帥,這個(gè)Masters可以又其他類(lèi)充當(dāng),因?yàn)樵擃?lèi)并不會(huì)直接偶合Warship類(lèi)
簡(jiǎn)單的來(lái)說(shuō),是哪個(gè)領(lǐng)導(dǎo)來(lái)統(tǒng)領(lǐng)戰(zhàn)爭(zhēng)都是允許的,該類(lèi)型作為命令的發(fā)送者,調(diào)用Command中的excuteCommand方法
commandes屬性作為容器來(lái)裝載(記錄)命令的執(zhí)行順序,如果想直接操作命令隊(duì)列中命令的增刪改查可以改為公開(kāi)屬性
setOrders:方法可以設(shè)置多個(gè)方法讓warship按順序執(zhí)行
notify 通知軍艦按照順序執(zhí)行命令
#import <Foundation/Foundation.h>
#import "CDCommand.h"
@interface CDMasters : NSObject
//帶多一個(gè)屬性可以設(shè)置聯(lián)合攻擊,否則每次一設(shè)置攻擊后都要調(diào)用notify不太方便
@property (nonatomic , strong ) NSMutableArray<id<CommandProtocol>> *commandes;
-(void)setOrders:(id<CommandProtocol>)command;
-(void)notify;
@end
CDMasters.m
#import "CDMasters.h"
@interface CDMasters()
@end
@implementation CDMasters
- (instancetype)init
{
self = [super init];
if (self) {
_commandes = [NSMutableArray array];
}
return self;
}
-(void)setOrders:(id<CommandProtocol>)command{
[_commandes addObject:command];
}
-(void)notify{
[_commandes enumerateObjectsUsingBlock:^(id<CommandProtocol> _Nonnull command, NSUInteger idx, BOOL * _Nonnull stop) {
[command excuteCommand];
}];
}
@end
Test.m
客戶(hù)端調(diào)用代碼如下:
CDWarship* warship = [[CDWarship alloc]init];
CDCommand* attShip = [[CDAttackShipCommand alloc] initWithWarship:warship];
CDCommand* attPlane = [[CDAttackAirplaneCommand alloc] initWithWarship:warship];
CDCommand* defense = [[CDDefenseCommand alloc]initWithWarship:warship];
CDMasters* master = [[CDMasters alloc]init];
//每下一次命令攻擊一次
//攻擊潛艇
[master setOrders:attShip];
//攻擊飛機(jī)
[master setOrders:attPlane];
//攻擊完后進(jìn)行防御
[master setOrders:defense];
[master notify];
//打印結(jié)果為:
//發(fā)射魚(yú)雷,擊毀對(duì)方潛艇
//發(fā)射防空導(dǎo)彈,擊毀對(duì)方戰(zhàn)斗機(jī)
//防御狀態(tài)
3.用途
- 比較容設(shè)計(jì)出一個(gè)命令隊(duì)列,并且這個(gè)命令隊(duì)列可操作
- 在需要的情況下,可以比較容易地將命令寫(xiě)入日志
- 允許接受請(qǐng)求的一行決定是否要否決請(qǐng)求
- 對(duì)于對(duì)象的請(qǐng)求,比較容易實(shí)現(xiàn)撤銷(xiāo)和重做
- 新加入的具體命令類(lèi)不影響其他類(lèi),擴(kuò)展性良好
- 命令模式把請(qǐng)求一個(gè)操作的對(duì)象與知道怎么執(zhí)行一個(gè)操作的對(duì)象分割開(kāi)
六.橋接模式
1.定義
橋接模式(Bridge),將抽象部分與它的實(shí)現(xiàn)部分分離,使得它們都可以獨(dú)立的變化(實(shí)現(xiàn)指的是抽象類(lèi)和它的派生類(lèi)用來(lái)實(shí)現(xiàn)自己的對(duì)象)
這里面簡(jiǎn)單的來(lái)說(shuō)是抽象基類(lèi)(命名為X)里面有一個(gè)屬性,而這個(gè)屬性也屬于抽象基類(lèi)(命名為Y),用常用語(yǔ)言來(lái)描述是X擁有Y.
由于這種擁有關(guān)系是抽象的.我們可以有X的子類(lèi)X-A 和X-B,Y也有自己的子類(lèi)Y-A和Y-B.
既然X可以擁有Y,那么實(shí)際上X-A可以擁有Y-A或Y-B,同樣X(jué)-B也可以擁有Y-A或Y-B,這里我們可以發(fā)現(xiàn)有種組合關(guān)系的感覺(jué),確實(shí)也如此
所謂的橋接模式,就是利用兩個(gè)抽象類(lèi)的關(guān)系,來(lái)讓子類(lèi)們可以有著各種各樣的組合基類(lèi)X和Y只是二維表現(xiàn),實(shí)際上我們還可以擁有三維或者更多維的組合關(guān)系,利用這種方法可以靈活的配置類(lèi)和屬性類(lèi)的關(guān)系.
2.模式
(待續(xù)....)