iOS設(shè)計(jì)模式

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. 用途:

  1. 策略模式可以用來(lái)定義一系列是算法,對(duì)外提供的接口是相同的,只是改變了內(nèi)部的實(shí)現(xiàn)即產(chǎn)生了不同的效果,核心作用就是用于封裝算法
  2. 原本我們可以將所有操作封裝在一個(gè)類(lèi)里面,每次增加策略就需要在里面增加if else或者switch語(yǔ)句,但是你會(huì)發(fā)現(xiàn)這樣耦合度很高,不方便單個(gè)算法的測(cè)試--從而也引出了策略模式的另外一個(gè)好處:簡(jiǎn)單化了單元測(cè)試,每一個(gè)算法就是一個(gè)
  3. 個(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. 用途:

  1. 裝飾者模式可以理解為在一個(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é)單一的原則)
  2. 把核心功能和其他功能區(qū)分開(kāi),方便拆裝和管理
  3. 個(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.用途

  1. 實(shí)際上是簡(jiǎn)單工廠模式的抽象和推廣,克服了簡(jiǎn)單工廠模式違背了封閉-開(kāi)放原則,又保持了封裝對(duì)象的優(yōu)點(diǎn)
  2. 把從工廠的內(nèi)部判斷移動(dòng)到了控制器,產(chǎn)生子類(lèi)的選擇更加靈活
  3. 個(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.用途

  1. 需要重復(fù)創(chuàng)建對(duì)象的時(shí)候可以考慮使用原型模式
  2. 原型模式也屬于創(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.用途

  1. 比較容設(shè)計(jì)出一個(gè)命令隊(duì)列,并且這個(gè)命令隊(duì)列可操作
  2. 在需要的情況下,可以比較容易地將命令寫(xiě)入日志
  3. 允許接受請(qǐng)求的一行決定是否要否決請(qǐng)求
  4. 對(duì)于對(duì)象的請(qǐng)求,比較容易實(shí)現(xiàn)撤銷(xiāo)和重做
  5. 新加入的具體命令類(lèi)不影響其他類(lèi),擴(kuò)展性良好
  6. 命令模式把請(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ù)....)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 幾乎在每個(gè)用面向?qū)ο笳Z(yǔ)言寫(xiě)的應(yīng)用程序里都能看到工廠方法。工廠方法模式是抽象工廠模式的組成部分。各種具體工廠重載其抽...
    live111閱讀 418評(píng)論 0 1
  • 小白程序員只能看懂源代碼,而大神程序員能看懂文檔。 設(shè)計(jì)模式:為解決特定場(chǎng)景的問(wèn)題而定制的解決方案。設(shè)計(jì)原則:構(gòu)建...
    印林泉閱讀 970評(píng)論 0 8
  • Design Pattern 設(shè)計(jì)模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類(lèi)的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。設(shè)計(jì)模式...
    賣(mài)萌的二師兄閱讀 679評(píng)論 0 3
  • 這次我們要遵循兩個(gè)基本原則:找出 #變化 封裝之優(yōu)先使用對(duì)象聚集,而不是繼承 1.適配器 用于連接兩種不同的東西,...
    孢子菌閱讀 613評(píng)論 0 0
  • Review 97-99
    Lifefullofjoy閱讀 274評(píng)論 0 0

友情鏈接更多精彩內(nèi)容