筆記 - 設計模式

設計模式:爲特定場景下的問題而定製的解決方案


一、對象創建
  • 原型
  • 工廠方法
  • 抽象工廠
  • 簡單工廠(不屬於重點設計模式)
  • 生成器
  • 單例
二、接口適配
  • 適配器
  • 橋接
  • 外觀
三、對象去耦
  • 中介者
  • 觀察者
四、抽象集合
  • 組合
  • 迭代器
五、行爲擴展
  • 訪問者
  • 裝飾
  • 責任鏈
六、算法封裝
  • 模版方法
  • 策略
  • 命令
七、性能與對象訪問
  • 享元
  • 代理
八、對象狀態
  • 備忘錄

一、對象創建

1、原型(Prototype)✅

使用原型實例制定創建對象的種類,並通過複製這個原型創建新的對象。

總結:
- 可以理解爲模板。在創建新對象的時候,按照模板的方法來複制,避免重複造輪子。

優點:
- 可以避免內存較大開銷的初始化創建動作
- 不用重新初始化對象,而是動態的獲取對象運行時的狀態


OC中的實現:
- 深拷貝行爲。⚠️需要遵循 NSCoding 協議
- UITableViewCell 重用機制也可以理解爲使用原型模式,通過cell模板來複制cell。

- (instancetype)copyWithZone:(NSZone *)zone {
    WJPersonal *itemInfo = [[[self class] allocWithZone:zone] init];
    itemInfo.serverInfo = [self.serverInfo copy];
    itemInfo.checkID = self.checkID;
    itemInfo.checkName = self.checkName;
    itemInfo.checkStatus = self.checkStatus;
    return itemInfo;
}

2、工廠方法✅

定義創建對象的接口,讓子類決定實例化哪一個類。工廠方法使得一個類的實例化延遲到其子類。

- 也稱爲虛構造器,⚠️與抽象工廠的區別

優點:
- 從代碼中消除了對應用程序特有類的耦合


何時使用工廠方法:
- 編譯時無法準備預期要創建的對象的類
- 類想讓其子類決定再運行時創建什麼
- 類有若干輔助類爲其子類,而你想將返回哪個子類型這一信息局部化


Cocoa Touch框架中應用的工廠方法:
+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
+ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
+ (NSNumber *)numberWithInt:(int)value;
...

3、抽象工廠✅

提供一個創建一系列相關或相互依賴對象的藉口,而無需制定他們具體的類

- 抽象工廠提了一個固定的藉口,用於創建一系列有關聯或相互依存的對象,而不必制定其具體類或其創建的細節
- 從工廠得到的具體對象之間沒有耦合


軟件設計的黃金法則:變動需要抽象


Cocoa Touch框架中應用抽象工廠:
NSNumber *boolNumber = [NSNumber numberWithBool:YES];
NSNumber *charNumber = [NSNumber numberWithChar:'A'];
NSNumber *intNumber = [NSNumber numberWithInt:1];
NSNumber *floatNumber = [NSNumber numberWithFloat:1.0];

NSLog(@"%@", [boolNumber class]);
NSLog(@"%@", [charNumber class]);
NSLog(@"%@", [intNumber class]);
NSLog(@"%@", [floatNumber class]);

2021-03-10 19:16:37.235427+0800 ZF_Beta[21994:439610] __NSCFBoolean
2021-03-10 19:16:37.235913+0800 ZF_Beta[21994:439610] __NSCFNumber
2021-03-10 19:16:37.236037+0800 ZF_Beta[21994:439610] __NSCFNumber
2021-03-10 19:16:37.236196+0800 ZF_Beta[21994:439610] __NSCFNumber
抽象工廠 工廠方法
通過對象組合創建抽象產品 通過類繼承創建抽象產品
創建多系列產品 創建一種產品
必須修改父類接口才能支持新的產品 子類化創建者並重載工廠方法以創建新產品
✨簡單工廠

專門定義一個類來負責創建其他類的實例,被創建的實例常常具有共同的父類

- 實際上就是由一個工廠類,根據傳入的參數,動態的決定創建出哪一個產品類的實例


優缺點:
- 客戶端可以直接消費產品,而不必關心具體產品的實現
- 消除了客戶端直接創建產品對象的責任,實現了對責任的分割。

- 工廠類集中了所有產品的創建邏輯,一旦不能正常工作,整個系統都會受到影響
- 當產品類別多結構複雜的時候,把所有創建工作放進一個工廠來,會使後期程序的擴展較爲困難


使用場景
- 工廠類負責創建的對象比較少時
- 客戶端只知道傳入工廠類的參數,對於如何創建對象的邏輯不必關心時

4、生成器(建造者模式)✅

將一個複雜對象的構建與它的表現分離,使得同樣的構建過程可以創建不同的表現

兩個角色:
- 指導者(Director)
- 生成器(Builder)
  • 創建一個電腦,分爲主機,屏幕,鍵盤,鼠標幾個部分
  • 產品:電腦
@interface WJComputer ()

@property (nonatomic, strong) NSString *screen;    //屏幕
@property (nonatomic, strong) NSString *host;      //主機
@property (nonatomic, strong) NSString *keyboard;  //鍵盤
@property (nonatomic, strong) NSString *mouse;     //鼠標

@end

@implementation WJComputer

- (instancetype)init {
    if(self = [super init]) {
        self.screen = @"屏幕";
        self.host = @"主機";
        self.keyboard = @"鍵盤";
        self.mouse = @"鼠標";
    }
    return self;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"主機爲%@, 屏幕爲%@,鍵盤爲%@,鼠標爲%@", self.host, self.screen, self.keyboard, self.mouse];
}

@en
  • 構建者
#import <Foundation/Foundation.h>
#import "WJComputer.h"

NS_ASSUME_NONNULL_BEGIN

@interface WJComputerBuilder : NSObject

@property (nonatomic, strong) WJComputer *computer;

- (WJComputerBuilder *)buildNewComputer;
- (WJComputerBuilder *)buildScreen:(NSString *)screen;
- (WJComputerBuilder *)buildHost:(NSString *)host;
- (WJComputerBuilder *)buildKeyboard:(NSString *)keyboary;
- (WJComputerBuilder *)buildMouse:(NSString *)mouse;

@end


@implementation WJComputerBuilder

- (WJComputerBuilder *)buildNewComputer {
    self.computer = [[WJComputer alloc] init];
    return self;
}

- (WJComputerBuilder *)buildScreen:(NSString *)screen {
    self.computer.screen = screen;
    return self;
}

- (WJComputerBuilder *)buildHost:(NSString *)host {
    self.computer.host = host;
    return self;
}

- (WJComputerBuilder *)buildKeyboard:(NSString *)keyboary {
    self.computer.keyboard = keyboary;
    return self;
}

- (WJComputerBuilder *)buildMouse:(NSString *)mouse {
    self.computer.mouse = mouse;
    return self;
}

@end
  • 指導者
#import <Foundation/Foundation.h>
#import "WJComputer.h"
#import "WJComputerBuilder.h"

NS_ASSUME_NONNULL_BEGIN

@interface WJComputerDirector : NSObject

- (WJComputer *)buildAcer:(WJComputerBuilder *)builder;
- (WJComputer *)buildLenovo:(WJComputerBuilder *)builder;

@end


@implementation WJComputerDirector

- (WJComputer *)buildAcer:(WJComputerBuilder *)builder {
    [builder buildNewComputer];
    [builder buildHost:@"AcerHost"];
    [builder buildScreen:@"AcerScreen"];
    [builder buildKeyboard:@"AcerKeyboard"];
    [builder buildMouse:@"AcerMouse"];
    return [builder computer];
}

- (WJComputer *)buildLenovo:(WJComputerBuilder *)builder {
    [builder buildNewComputer];
    [builder buildHost:@"lenovoHost"];
    [builder buildScreen:@"lenovoScreen"];
    [builder buildKeyboard:@"lenovoKeyboard"];
    [builder buildMouse:@"lenovoMouse"];
    return [builder computer];
}

@end
  • 調用及打印
WJComputerBuilder *creater = [[WJComputerBuilder alloc] init];
WJComputerDirector *director = [[WJComputerDirector alloc] init];

WJComputer *acre = [director buildAcer:creater];
NSLog(@"%@", acre);

WJComputer * lenovo = [director buildLenovo:creater];
NSLog(@"%@", lenovo);


2021-03-15 19:34:48.632250+0800 property[11469:275860] 主機爲AcerHost, 屏幕爲AcerScreen,鍵盤爲AcerKeyboard,鼠標爲AcerMouse
2021-03-15 19:34:48.632436+0800 property[11469:275860] 主機爲lenovoHost, 屏幕爲lenovoScreen,鍵盤爲lenovoKeyboard,鼠標爲lenovoMouse
  • 生成器與抽象工廠對比
生成器 抽象工廠
構建複雜對象 構建簡單或複雜對象
以多個步驟構建對象 以單一步驟構建對象
以多種方式構建對象 以單一方式構建對象
在構建過程的最後一步返回產品 立刻返回產品
專注一個特定的產品 強調一套產品

5、單例✅

保證一個類僅有一個實例,並提供一個訪問它全局訪問點

系統中只能共享不能複製的資源
- 定位。CLLocationManager定義了GPS設備所提供服務的單一訪問點


意圖:類的一個對象成爲系統中唯一的實例


Cocoa Touch框架使用單例
- UIApplication:每個應用程序有且僅有一個UIApplication實例,由UIApplicationMain函數在應用啓動時創建爲單例對象,之後通過shareApplication類方法訪問
- NSFileManager等


代碼中的使用
- 之所以實現NSCopying協議,與內存管理相關
- allocWithZone會分配實例的內存,引用計數會+1

+ (WJLocationManager *)sharedInstance {
    static WJLocationManager *s_instance = nil;
    static dispatch_once_t once_token;
    dispatch_once(&once_token, ^{
        s_instance =  [[super allocWithZone:NULL] init];
    });
    return s_instance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [WJLocationManager sharedInstance];
}

- (instancetype)copyWithZone:(struct _NSZone *)zone {
    return [WJLocationManager sharedInstance];
}

- (instancetype)mutableCopyWithZone:(nullable NSZone *)zone {
    return [WJLocationManager sharedInstance];
}

二、接口適配

6、適配器(Adapter 極爲常用)

將一個類的接口轉換爲客戶希望的另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作

使用情形
- 已由類的接口與需求不匹配
- 想要擁有一個可複用類,該類能夠同可能帶有不兼容接口的其他類協作
- 想要適配一個類的不同子類,可是讓每一個子類去子類化一個類適配器又不現實,可以使用對象適配器來適配其父類的接口


Cocoa Touch框架使用單例
- 委託(delegate)模式主要是適配器模式


- 類適配器
- 對象適配器
  • 類適配器對象適配器 特徵對比
類適配器 對象適配器
只針對單一的具體的Adaptee類,把Adaptee適配到Target 可以適配多個Adaptee及其子類
易於重載Adaptee的行爲 難以重載Adaptee的行爲,需要藉助於子類的對象而不是Adaptee本身
只有一個Adaptee對象,無需額外的指針間接訪問Adaptee 需要額外的指針以間接訪問Adaptee並適配其行爲

7、橋接

將抽象部分與它的實現部分分離,使他它們都可以獨立的變化

何時使用橋接模式
- 不想在抽象與其實現之間行程固定的綁定關係
- 抽象及其實現都可以通過子類化獨立進行擴展
- 對抽象的實現進行修改不影響客戶端代碼
- 如果每個實現需要額外的子類以細化抽象,則說明有必要把她們分成兩個部分
- 想在帶有不同抽象接口的多個對象之間共享一個實現

8、外觀(⚠️與組合模式的區別)✅

爲系統中的一組接口提供一個統一的接口。外觀定義了一個高層接口,讓子系統更易於使用

使用場景
- 子系統正逐漸變得複雜。應用模式的過程中演化出許多類。可以使用外觀爲這些子系統提供一個較簡單的接口
- 可以使用外觀對子系統進行分層。每個子系統級別有一個外觀座位入口點
  • 例:開機自動啓動XCode、微信、SourceTree...
#import "WJComputer.h"
#import "WJXcode.h"
#import "WJSourceTree.h"
#import "WJWeChat.h"

@interface WJComputer ()

@property (nonatomic, strong) WJXcode *Xcode;
@property (nonatomic, strong) WJSourceTree *sourceTree;
@property (nonatomic, strong) WJWeChat *weChat;

@end

@implementation WJComputer

- (instancetype)init {
    self = [super init];
    if (self) {
        self.xCode = [[WJXcode alloc] init];
        self.sourceTree = [[WJSourceTree alloc]init];
        self.weChat = [[WJWeChat alloc]init];
    }
    return self;
}

- (void)close {
    [self.xCode close];
    [self.sourceTree close];
    [self.weChat close];
}

- (void)open {
    [self.xCode open];
    [self.sourceTree open];
    [self.weChat open];
}

@end
@interface WJXcode : NSObject

- (void)close;
- (void)open;

@end

@implementation WJXcode

- (void)close {
    NSLog(@"%s", __func__);
}
- (void)open {
     NSLog(@"%s", __func__);
}

@end
@interface WJSourceTree : NSObject

- (void)close;
- (void)open;

@end

@implementation WJSourceTree

- (void)close {
    NSLog(@"%s", __func__);
}

- (void)open {
     NSLog(@"%s", __func__);
}

@end
@interface WJWeChat : NSObject

- (void)close;
- (void)open;

@end

@implementation WJWeChat

- (void)close {
    NSLog(@"%s", __func__);
}

- (void)open {
     NSLog(@"%s", __func__);
}

@end

三、對象去耦

9、中介者

用一個對象來封裝一系列對象的交互方式。中介者使各對象不需要顯示地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互

說明
- 中介者模式以中介者內部的複雜性代替交互的複雜性


使用場景
- 對象間的交互雖定義明確,然而非常複雜,導致一組對象比例相互依賴而且難以理解
- 因爲對象引用了許多其他對象並與其通訊,導致對象難以複用
- 想要定製一個分佈在多個類中的邏輯或行爲,又不想生成太多子類


例:推送路由處理
- 將跳轉方法封裝到一個類中,它減少了視圖控制器之間的依存關係
- (void)pushViewController:(NSInteger)tag {
    switch (tag) {
            case 0: {
                GuideViewController *guideViewController = [[GuideViewController alloc] init];
                [self.appRootController pushViewController:guideViewController animated:YES];
            }
            break;
            case 1: {
                WebViewController *webViewController = [[WebViewController alloc] init];
                [self.appRootController pushViewController:webViewController animated:YES];
            }
            break;

            ......

        default:
            break;
    }
}


**** 拓展:使用策略模式替換switch

10、觀察者✅

定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新

何時使用觀察者模式
- 有兩種抽象類型互相依賴。將他們封裝在各自的對象中,就可以對它們單獨進行改變和複用
- 對一個對象的改變需要同時改變其他對象,而不知道具體有多少對象有待改變
- 一個對象必須通知其他對象,而它又不需知道其他對象時什麼


Cocoa Touch框架中用到的觀察者模式
- 通知
- 鍵-值觀察者(KVO)

兩者的主要區別

通知 鍵-值觀察者
一箇中心對象爲所有觀察者提供變更通知 被觀察的對象直接面向觀察者發送通知
主要從廣義上關注程序事件 綁定於特定對象屬性的值

四、抽象集合

11、組合

將對象組合成樹形結構以表示“部分-整體”的結構層次。組合使用戶對單個對象和組合對象的使用具有一致性

- 通過組合模式實現多繼承


例:ClassC需要繼承ClassA中methodA、ClassB中methodB

@interface ClassA : NSObject 

-(void)methodA;

@end


@interface ClassB : NSObject

-(void)methodB;

@end



@interface ClassC : NSObject {
  ClassA *a;
  ClassB *b;
}

- (id)initWithA:(ClassA *)A b:(ClassB *)B;
- (void)methodA;
- (void)methodB;

@end

@implementation  ClassC

- (id)initWithA:(ClassA *)A b:(ClassB *)B {
       a = [[ClassA alloc] initWithClassA: A];
       b = [[ClassB alloc] initWithClassB: B];
}

- (void)methodA {
      [a methodA];
}

- (void)methodB {
      [b methodB];
}

12、迭代器✅

提供一種方法順序訪問一個聚合對象中的各個元素,而又不需暴露該對象的內部表示

外部迭代器
- 客戶端需要知道外部迭代器才能使用,但是它爲客戶端提供了更多的控制
- 客戶端創建並維護外部迭代器
- 客戶端可以使用不同外部迭代器實現多種類型的遍歷


內部迭代器
- 客戶端不需要知道任何外部迭代器,而是可以通過集合對象的特殊接口,或者一次訪問一個元素,或者向集合中的每個元素髮送消息
- 集合對象本身創建並維護它的外部迭代器
- 集合對象可以在不修改客戶端代碼的情況下,選擇不同的外部迭代器


何時使用迭代器模式
- 需要訪問組合對象的內容,而又不暴露其內部表示
- 需要通過多種方式便利組合對象
- 需要提供一個統一的接口,用來遍歷各種類的組合對象


Coco Touch框架中使用迭代器模式
- NSEnumerator
- 基於block的枚舉
- 快速枚舉
- 內部枚舉


NSArray *array = @[@1,@2,@3];
NSEnumerator *enumerator = [array objectEnumerator];
NSNumber *number;
while (number = [enumerator nextObject]) {
    /**
     *  do something
     */
}


for ( ... in ... )


NSArray *array = @[@"123",@"456",@"789"];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj localizedCaseInsensitiveCompare:@"789"]) {
        *stop = YES;
    }
}];

五、行爲擴展

13、訪問者

表示一個作用於某對象結構中的各元素的操作。它讓我們可以在不改變各元素的類的前提下定義作用於這些元素的新操作

使用場景:
- 一個複雜的對象結構包含很多其他對象,它們有不同的接口,但是想對這些對象實施一些依賴於其具體類型的操作
- 需要對一個組合結構中的對象進行很多不相關的操作,但是不想讓這些操作“污染”這些類的對象
- 可以將相關的操作集中起來,定義在一個訪問者類中,並在需要在訪問者中定義的操作時使用它
- 定義複雜結構的類很少作修改,但經常需要向其添加新的操作


關鍵角色
- 訪問者
- 它訪問的元素


優點:
- 添加操作,只需實現具體的訪問者,不會對類的結構造成破壞


缺點:
- 訪問者與目標類耦合在一起,因此,如果訪問者需要支持新類,訪問者的父類和子類都需要修改,才能反應新的功能

14、裝飾✅

動態地給一個對象添加一些額外的職責。就是擴展功能來說,裝飾模式相比生成子類更爲靈活

使用場景:
- 想要在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責
- 想要擴展一個類的行爲,卻做不到。類定義可能被隱藏,無法進行子類化;或者進行子類化;或者,對類的每個行爲的擴展,爲支持每種功能的組合,將產生大量的子類
- 對類的職責的擴展是可選的


總結:
- 裝飾者包含被裝飾者的所有接口和引用,方法實現完全是引用調用自己的方法,在裝飾者子類添加新功能。
- 分類主要用於對被裝飾者類的方法拓展,與本設計模式稍有區別


使用:
- 爲減少第三方的入侵性,通常創建一個類來管理它
- IQKeyboardManager


@interface IQKeyboardManager : NSObject

@property(nonatomic, assign, getter = isEnabled) BOOL enable;
@property(nonatomic, assign) CGFloat keyboardToNavigationDistance;
...

@end



@interface WJKeyboardManager : NSObject

@property(nonatomic, assign, getter = isEnabled) BOOL enable;
@property(nonatomic, assign) CGFloat keyboardToNavigationDistance;

@property (nonatomic, strong) IQKeyboardManager *keyboardMgr;

@end


@implementation WJKeyboardManager

- (instancetype)init {
    self = [super init];
    if (self) {
        _keyboardMgr = [IQKeyboardManager sharedManager];
    }
    return self;
}

- (void)setEnable:(BOOL)enable {
    [_keyboardMgr setEnable:enable];
}

- (BOOL)isEnabled {
    return _keyboardMgr.isEnabled;
}

- (BOOL)keyboardIsShowing {
    return _keyboardMgr.keyboardShowing;
}

- (void)setKeyboardToNavigationDistance:(CGFloat)keyboardToNavigationDistance {
    [_keyboardMgr setKeyboardToNavigationDistance:keyboardToNavigationDistance];
}

  • 裝飾和策略之間的差異
“外表”變更(裝飾) “內容”變更(策略)
從外部變更 從內部變更
每個節點不知道變更 每個節點知道一組預定義的變更方式

15、責任鏈✅

使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間發生耦合。此模式將這些對象連成一條鏈,並沿着這條鏈傳遞請求,知道有一個對象處理它爲止。

何時使用責任鏈模式
- 有多個對象可以處理請求,而處理程序只有在運行時才能確定
- 向一組對象發送請求,而不想顯式指定處理請求的特定處理程序


例:遊戲中人物的鏈式防禦層
- 盔甲1不知道如何應對攻擊,所以把它傳給下一個盔甲
- 盔甲2剛好知道如何應對這次攻擊,化解了人物可能遭受的損傷

優點:
- 任何攻擊處理程序都能在任何時間被添加/刪除,而不會影響人物的其他行爲


開發中的應用:
- 用戶的信息錄入校驗(層層校驗)

六、算法封裝

16、模版方法✅

定義一個操作中算法的骨架,而將一些步驟延遲到子類中。模版方法使子類可以重定義算法的某些特定步驟而不改變算法的結構

何時使用模版方法
- 需要一次性實現算法的不變部分,並將可變的行爲留給子類來實現
- 子類的共同行爲應該被提取出來放到公共類中,以避免代碼重複
- 需要控制子類的擴展


Cocoa Touch框架中用到的模版方法
- UIView類中的定製繪圖(- (void)drawRect:(CGRect)rect)


簡單總結:
- 將共有實現抽到父類方法
- 重寫父類方法,實現自己特有,不需要執行[super text];
模版 委託(適配器)
父類定義一個一般算法,但缺少某些特定/可選的信息或算法,它通過這些缺少的信息或算法起到一個算法“食譜”的作用 委託(適配器)與預定義好的委託接口一起定義一個特定算法
缺少的信息由子類通過繼承來提供 特定算法由任何對象通過對象組合來提供

17、策略✅

定義一系列算法,把他們一個個封裝起來,並且使它們可以相互替換。本模式使得算法可獨立於使用它的客戶而變化

  • 把每個算法封裝成一個對象,消除根據數據類型決定使用什麼算法的一堆if-else或switch-case
  • 如果代碼中有很多條件語句,就可能意味着需要把它們重構成各種策略對象
  • 優點是:解決代碼的耦合度,但是爲了解決代碼耦合度,需要創建更多的類。
何時使用策略模式
- 一個類在其操作中使用多個條件語句來定義許多行爲。我們可以把相關的條件分支移到它們自己的策略類中
- 需要算法的各種變體
- 需要避免把複雜的、與算法相關的數據結構暴露給客戶端


應用
- collectionview佈局
- UITextField驗證

// 使用策略模式前
- (void)textFieldDidEndEditing:(UITextField *)textField {
    if (textField == self.letterInput) { // 驗證輸入是否爲字母
        NSString *outputLatter = [self validateLatterInput:textField];
        if (outputLatter) {
            ...
        } else {
            ...
        }
    } else if (textField == self.numberInput) { // 驗證輸入的是否爲數字
        NSString *outputNumber = [self validateNumberInput:textField];
        if (outputNumber) {
            ...
        } else {
            ...
        }
    }
    ....
}


// 使用策略模式後:將所有邏輯判斷都封裝到類中
- (void)textFieldDidEndEditing:(UITextField *)textField {
    if ([textField isKindOfClass:[CustomTextField class]]) {
        [(CustomTextField *)textField validate];
    }
}

18、命令

將請求封裝爲一個對象,從而可用不同的請求對客戶進行參數化,對請求排隊或記錄請求日誌,以及支持可撤銷的操作

何時使用命令模式
- 想讓應用程序支持撤銷與恢復
- 想讓對象參數化一個動作以執行操作,並用不同命令對象來代替回調函數
- 想要在不同時刻對請求進行指定、排列和執行
- 想記錄修改日誌,這樣在系統故障時,這些修改可在後來重做一遍
- 想讓系統支持事物,事物封裝了對數據的一系列修改。事物可以建模爲命令對象


Cocoa Touch框架中使用命令模式
- NSUnvotaion
- NSUndoManager


命令模式還能做什麼?
- 推遲調用器的執行
- 例,點擊控制器的按鈕,可以執行一個命令對戲那個,對另一個試圖控制器進行某些操作。命令對象隱藏了與這些操作有關的所有細節

七、性能與對象訪問

19、享元✅

運用共享技術有效地支持大量細粒度的對象

何時使用享元模式
- 應用程序使用很多對象
- 在內存中保存對象回影響內存性能
- 對象的多數特有狀態可以放到外部而輕量化
- 移除了外在對象之後,可以用較少的共享對象代替原來的那組對象
- 應用程序不依賴於對象標識,因爲共享對象不能提供唯一的標識


關鍵組件
- 享元對象
- 保存享元對象的池


優點:節省空間


節省空間主要取決於這幾個因素:
- 通過共享減少的對象總數
- 每個對象中內在狀態的數量
- 外在狀態是計算出來的還是保存的


應用:書中提到的“百花池”程序
- 用6個不同的實例,畫很多隨機尺寸和位置的花

20、代理

爲其他對象提供一種代理以控制對這個對象的訪問

- 我們平時使用的@protocol,遵循協議,成爲代理,實現方法

@protocol WJIMObject <NSObject>

- (NSString *)imUUID;
- (NSString *)imAuthToken;
- (NSString *)imHost;

@end


Cocoa Touch框架中使用代理模式
- NSProxy🚩🚩🚩🚩🚩

八、對象狀態

21、備忘錄

在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態

何時使用備忘錄模式
- 需要保存一個對象在某個時刻的狀態,這樣以後就可以恢復到先前的狀態
- 用於獲取狀態的接口會暴露實現的細節,需要將其隱藏起來


Cocoa Touch框架中的備忘錄模式
- 歸檔(🚩🚩🚩🚩🚩)
- 屬性列表序列化
- 核心數據
九、複合設計模式
  • MVC
  • MVVM
  • MVP

結尾:

  • 參考:《Objective-C編程之道》、《大話數據結構》
  • 關於MVC
- MVC本身並不是最基本的設計模式
- 它包含了若干更加基本的設計模式
- 在MVC中,基本設計模式互相配合,確定了各功能之間的協作

Cocoa的MVC用到的模式有:**組合、命令、中介者、策略、觀察者**
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章