iOS-设计模式简析

参考:《Objective-C编程之道》

前言

这边文章是在学习了《Objective-C编程之道》后的做的一些笔记,在其中参考了其他相关书籍或者其他前辈的例子以及一些文章内容,有引用到的地方我已经尽量添加原文链接或者引用了,如果发现本文侵权可联系删除.

此篇文章主要是对各个模式对介绍,此处引用对例子是我认为比较合适比较容易理解对例子,不过也会有一些片面,无法完全解决各个模式.这篇文章总的来说适合用来入门设计模式.

此篇文章可能会有很多不足之处,欢迎大家指出批评,也欢迎大家一起来学习探讨.

对象创建

原型

1.概述

使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象.

2.使用场景
  • 需要创建的对象应独立于其类型与创建方式.
  • 要实例化的类是在运行时决定的.
  • 不想要与产品层次相对应的工厂层次.
  • 不同类的实例间的差异仅是状态的若干组合因此复制相应数量的原型比手工实例化更加方便.
  • 类不容易创建,比如每个组件可把其他组件作为子节点的组合对象.复制已有的组合对象并对副本进行修改会更加容易.
3.结构UML图

4.实例

数据模型间的复制

    Student *s1 = [[Student alloc] init];
    s1.age = 18;
    s1.name = @"s1";
    s1.address = @"adress";
    s1.size = CGSizeMake(100, 100);
    s1.teacher = p1;
    s1.friends = @[p1, p2];
    s1.girlfriends = [[NSMutableArray alloc] initWithArray:s1.friends];
    s1.other = @[s1.friends, s1.girlfriends];
    
    Student *s2 = [s1 copy];
复制代码

这里通过copy能够拷贝得到一个数据一致的Student.

这里需要特别注意的是浅复制深复制之间的区别.

工厂方法

1.概述
  • 工厂方法的最初定义是专注于让子类决定创建什么对象.
  • 工厂方法也称为虚构造器.它适用于这种情况:一个类无法预期需要生成哪个类的对象,想让子类来指定所生成的对象.
  • 定义创建对象的接口,让子类决定实例化哪一个类.工厂方法使得一个类的实现化延迟到其子类.
2.使用场景
  • 编译时无法准确预期要创建的对象的类
  • 类想让其子类决定在运行时要创建什么
  • 类有若干辅助类为其子类,而你想将返回哪一个子类这一信息局部化
3.结构UML图

4.实例: 创建工厂类,开放接口,生成对应的类
+ (UITableViewCell *)cellWithText:(NSString *)text {
    LabelTableViewCell *cell = [[LabelTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
    cell.textLabel.text = text;
    return cell;
}

+ (UITableViewCell *)cellWithImage:(UIImage *)img {
    ImageTableViewCell *cell = [[ImageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    cell.imageView.image = img;
    return cell;
}

+ (UITableViewCell *)cellWithSelect:(BOOL)isSelect {
    SwitchTableViewCell *cell = [[SwitchTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    [cell setSwitchSelect:isSelect];
    return cell;
}
复制代码

iOS中可以参考系统类NSNumber;

- (NSNumber *)initWithChar:(char)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedChar:(unsigned char)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithShort:(short)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedShort:(unsigned short)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithInt:(int)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedInt:(unsigned int)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithLong:(long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedLong:(unsigned long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithLongLong:(long long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedLongLong:(unsigned long long)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithFloat:(float)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithDouble:(double)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithBool:(BOOL)value NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithInteger:(NSInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (NSNumber *)initWithUnsignedInteger:(NSUInteger)value API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;

复制代码

抽象工厂

1.概述

提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类.

2.结构UML图

3.代码实例:创建不同类型Cell

抽象工厂AbstractFactory


@interface AbstractFactory : NSObject

+ (UITableViewCell *)cellWithText:(NSString *)text;
+ (UITableViewCell *)cellWithImage:(UIImage *)img;

@end
复制代码

继承AbstractFactory的普通工厂CellFactory

@implementation CellFactory

+ (UITableViewCell *)cellWithText:(NSString *)text {
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
    cell.textLabel.text = text;
    return cell;
}

+ (UITableViewCell *)cellWithImage:(UIImage *)img {
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    cell.imageView.image = img;
    return cell;
}

@end
复制代码

继承AbstractFactory的可点击Cell工厂CellFactory

@implementation ClickFactory

+ (UITableViewCell *)cellWithText:(NSString *)text {
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
    cell.textLabel.text = text;
    cell.textLabel.userInteractionEnabled = YES;
    return cell;
}

+ (UITableViewCell *)cellWithImage:(UIImage *)img {
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    cell.imageView.image = img;
    cell.imageView.userInteractionEnabled = YES;
    return cell;
}

@end

复制代码

控制器实现

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;
    
    switch (indexPath.row % 2) {
        case 0:
            if (indexPath.row > 2) {
                cell = [ClickFactory cellWithText:[NSString stringWithFormat:@"%ld", indexPath.row]];
            } else {
                cell = [CellFactory cellWithText:[NSString stringWithFormat:@"%ld", indexPath.row]];
            }
            break;
        case 1:
            if (indexPath.row > 2) {
                cell = [ClickFactory cellWithImage:[UIImage imageNamed:@"12.png"]];
            } else {
                cell = [CellFactory cellWithImage:[UIImage imageNamed:@"12.png"]];
            }
            break;
        case 2:
            break;
        default:
            break;
    }
    return cell;
}
复制代码
4.工厂模式抽象工厂模式对比

抽象工厂工厂模式在许多方面都非常类似.很多人常常搞不清楚应该在什么时候用哪一个.两个模式都用于相同的目的:创建对象而不让客户端知晓返回了什么确切的具体对象.

抽象工厂工厂方法
通过对象组合创建抽象产品通过类继承创建抽象产品
创建多系列产品创建一种产品
必须修改父类的接口才能支持新的产品子类化创建者并重载工厂方法以创建新产品
5.总结

抽象工厂模式是一种极为常见的设计模式.它是最基本的,因为它可以涉及许多类型的对象创建.一系列相关类的好的模式,应该作为一种抽象,不为客户端所见.抽象工厂可以顺畅地提供这种抽象,而不暴露创建过程中任何不必要的细节或所创建对象的确切类型.

生成器

1.概述

将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现.

2.使用场景
  • 需要创建涉及各种部件的复杂对象.创建对象的算法应该独立于部件的装配方式.常见例子是构建组合对象.

  • 构建过程需要以不同的方式构建对象.

3.结构UML图以及交互时序图

4.代码实例:制作三明治

定义指导者SandwichDirector

@interface SandwichDirector : NSObject

// 具体的生成类
@property (nonatomic, strong) id<SandwichBuilder> concreteBuilder;

// 生成动作
- (void)construct;

@end

规定制作三明治流程

@implementation SandwichDirector

- (void)construct {
    [_concreteBuilder prepareBread];
    [_concreteBuilder addMeat];
}

@end
复制代码
@protocol SandwichBuilder <NSObject>

- (void)prepareBread;
- (void)addMeat;

@end
复制代码

基础三明治类Sandwich

@interface Sandwich : NSObject

@property (nonatomic, copy) NSString *breadType;
@property (nonatomic, copy) NSString *meatType;

@end

复制代码

制作鲁宾三明治类SandwichConcreteBuilder

@interface SandwichConcreteBuilder : NSObject<SandwichBuilder>

// 获取生成的三明治
- (Sandwich *)getSandwich;

@end

@interface SandwichConcreteBuilder ()

@property (nonatomic, strong) Sandwich *reubenSandwich;


@end

@implementation SandwichConcreteBuilder

- (instancetype)init {
    if (self = [super init]) {
        _reubenSandwich = [[Sandwich alloc] init];
    }
    return self;
}

- (void)prepareBread {
    _reubenSandwich.breadType = @"黑麦面包";
}

- (void)addMeat {
    _reubenSandwich.meatType = @"腌牛肉";
}

- (id)getSandwich {
    return _reubenSandwich;
}

复制代码

获取鲁宾三明治

    SandwichDirector *director = [[SandwichDirector alloc] init];
    SandwichConcreteBuilder *builder = [[SandwichConcreteBuilder alloc] init];
    [director setConcreteBuilder:builder];
    [director construct];
    
    Sandwich *sandwich = [builder getSandwich];
    NSLog(@"面包:%@\n肉:%@", sandwich.breadType, sandwich.meatType);
复制代码
5.总结

生成器模式能帮助构建涉及部件与表现的各种组合的对象.没有这一模式,知道构建对象所需细节的Director可能最终会变成一个庞大的"神"类,带有无数用于构建同一个类的各种表现的内嵌算法.

单例

1.概述

保证一个类仅有一个实例,并提供一个访问它的全局访问点.

2. 使用场景
  • 类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方法.
  • 这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码.
  • 需要提供一个统一的接口,用来遍历各种类型的组合对象.
3.结构UML图

4.代码实例:简单单例实现
@interface Singleton : NSObject

/// 统一的接口
+ (instancetype)defaultSingleton;

@end

static Singleton *_singleton;

@implementation Singleton

+ (instancetype)defaultSingleton {
    return [[self alloc] init];
}

- (instancetype)init {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _singleton = [super init];
    });
    return _singleton;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (!_singleton) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _singleton = [super allocWithZone:zone];
        });
    }
    return _singleton;
}

- (id)copy {
    return _singleton;
}

- (id)mutableCopy {
    return _singleton;
}

@end

复制代码

因为单例只会存在一个对象,所以sigleton0sigleton1等价的.

    Singleton *sigleton0 = [[Singleton alloc] init];
    NSLog(@"%@", sigleton0);
    
    Singleton *sigleton1 = [Singleton defaultSingleton];
    NSLog(@"%@", sigleton1);
    
    NSLog(@"%@", [sigleton0 copy]);
    NSLog(@"%@", [sigleton1 mutableCopy]);
复制代码
5.总结

几乎在任何类型的应用程序中,单例模式都极为常见,并不只限于iOS应用程序开发.只要应用程序需要用集中式的类来协调其服务,这个类就应生成单一的实例,而不是多个实例.

接口适配

适配器

1.概述

将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.

2.使用场景
  • 已有类的接口与需求不匹配
  • 想要一个可复用的类,该类能够同可能带有不兼容接口的其他类写作.
  • 需要适配一个类的几个不同子类,可是让每一个子类去子类化一个类适配器又不现实.那么可以使用对象适配器(也叫委托)来适配其父类的接口.
3.结构UML图

类适配器类图

对象适配器类图

4.代码实例简单适配器实现

参考: Objective C--适配器模式

初始请求目标执行类

@interface Target : NSObject

- (void)request;

@end

@implementation Target

- (void)request {
    NSLog(@"请求");
}

@end
复制代码

新的执行类

@interface Adaptee : NSObject

- (void)specialRequest;

@end

@implementation Adaptee

- (void)specialRequest {
    NSLog(@"特殊请求");
}

@end
复制代码

适配器类实现

@interface Adapter : Target

@property (nonatomic, strong) Adaptee *adaptee;

@end

@implementation Adapter

- (instancetype)init {
    if (self = [super init]) {
        self.adaptee = [[Adaptee alloc] init];
    }
    return self;
}

- (void)request {
    [self.adaptee specialRequest];
}

@end

复制代码

控制器调用

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Target *target = [[Adapter alloc] init];
    [target request];
}

@end
复制代码
5.总结

适配器模式可以用作为目标接口的协议来实现,协议定义客户端要求的,也是适配器可以保证的一些标准行为.协议Object-C中语言级别的功能,通过它可以定义用作适配器模式的实例的接口.

桥接

1.概述

将抽象部分与它的实现部分分离,使它们都可以独立地变化.

2.使用场景
  • 不想在抽象与其实现之间形成固定的绑定关系.
  • 抽象及其实现都应可以通过子类化独自进行扩展.
  • 对抽象的实现进行修改不应影响客户端代码.
  • 如果每个实现需要额外的子类以细化抽象,则说明有必要把它们分成两个部分.
  • 想在带有不同抽象接口的多个对象之间共享一个实现.
3.结构UML图

4.代码实例 通过手机SMS发送QQ消息或者普通消息.

参考:设计模式系列 11-- 桥接模式

接口协议MessageImplement

@protocol MessageImplement <NSObject>

- (void)sendMessage:(NSString *)message;

@end
复制代码

抽象信息类AbstractMessage

/// 抽象信息类
@interface AbstractMessage : NSObject

@property (nonatomic, strong) id<MessageImplement> messageIm;

- (void)send:(NSMutableString *)message;
- (instancetype)initWithImplement:(id<MessageImplement>)implement;

@end

@implementation AbstractMessage

- (instancetype)initWithImplement:(id<MessageImplement>)implement {
    if (self = [super init]) {
        self.messageIm = implement;
    }
    return self;
}

- (void)send:(NSMutableString *)message {
    
}

@end
复制代码

普通信息类CommonMessage

/// 普通信息类
@interface CommonMessage : AbstractMessage

@end

@implementation CommonMessage

- (void)send:(NSMutableString *)message {
    [message insertString:@"【普通消息:" atIndex:0];
    [message appendString:@"】"];
    [self.messageIm sendMessage:message];
}

@end
复制代码

QQ信息类QQMessage

/// QQ信息类
@interface QQMessage : AbstractMessage

@end

@implementation QQMessage

- (void)send:(NSMutableString *)message {
    [message insertString:@"【QQ消息:" atIndex:0];
    [message appendString:@"】"];
    [self.messageIm sendMessage:message];
}

@end
复制代码

系统发送类MessageSMS

/// 系统发送类
@interface MessageSMS : NSObject<MessageImplement>

@end

@implementation MessageSMS

- (void)sendMessage:(NSString *)message {
    NSLog(@"使用系统发送消息:%@", message);
}

@end
复制代码

手机发送类MessageTEL

/// 手机发送类
@interface MessageTEL : NSObject<MessageImplement>

@end

@implementation MessageTEL

- (void)sendMessage:(NSString *)message {
    NSLog(@"使用手机发送消息:%@", message);
}

@end
复制代码

控制器实现

- (void)viewDidLoad {
    [super viewDidLoad];
    
    MessageSMS *sms = [[MessageSMS alloc] init];
    MessageTEL *tel = [[MessageTEL alloc] init];
    
    QQMessage *messageSMS = [[QQMessage alloc] initWithImplement:sms];
    [messageSMS send:[[NSMutableString alloc] initWithString:@"SMS->QQ"]];
    NSLog(@"\n");
    
    QQMessage *messageTEL = [[QQMessage alloc] initWithImplement:tel];
    [messageTEL send:[[NSMutableString alloc] initWithString:@"TEL->QQ"]];
    NSLog(@"\n");

    CommonMessage *commonSMS = [[CommonMessage alloc] initWithImplement:sms];
    [commonSMS send:[[NSMutableString alloc] initWithString:@"SMS->Common"]];
    NSLog(@"\n");

    CommonMessage *commonTEL = [[CommonMessage alloc] initWithImplement:tel];
    [commonTEL send:[[NSMutableString alloc] initWithString:@"TEL->Common"]];
}

@end
复制代码

结果展示

2019-04-10 22:25:22.611392+0800 BridgePattern[46027:874792] 使用系统发送消息:【QQ消息:SMS->QQ】
2019-04-10 22:25:22.611661+0800 BridgePattern[46027:874792] 
2019-04-10 22:25:25.998905+0800 BridgePattern[46027:874792] 使用手机发送消息:【QQ消息:TEL->QQ】
2019-04-10 22:25:25.999066+0800 BridgePattern[46027:874792] 
2019-04-10 22:25:25.999229+0800 BridgePattern[46027:874792] 使用系统发送消息:【普通消息:SMS->Common】
2019-04-10 22:25:25.999343+0800 BridgePattern[46027:874792] 
2019-04-10 22:25:25.999468+0800 BridgePattern[46027:874792] 使用手机发送消息:【普通消息:TEL->Common】
复制代码
5.总结

桥接模式是把一个接口适配到不同接口的一种方式.

外观

1.概述

为系统中的一组接口提供一个统一的接口.外观定义一个高层接口,让子系统更易于使用.

2.使用场景
  • 子系统正逐渐变得复杂.应用模式的过程中演化出许多类.可以使用外观为这些子系统提供一个简单的接口.

  • 可以使用外观对子系统进行分层.每个子系统级别有一个外观作为入口点.让它们通过其外观进行通信,可以简化它们的依赖关系.

3.结构UML图iOS 设计模式-结构模式-外观模式

4.代码实例-- 出租车为驾驶出租车的一组复杂接口提供一个简化的接口

定义汽车类Car

@interface Car : NSObject

- (void)releaseBreaks;
- (void)changeGears;
- (void)pressAccelerator;
- (void)pressBreaks;
- (void)releaseAccelerator;

@end


@implementation Car

- (void)releaseBreaks {
    NSLog(@"松开刹车");
}

- (void)changeGears {
    NSLog(@"换挡");
}

- (void)pressAccelerator {
    NSLog(@"踩油门");
}

- (void)pressBreaks {
    NSLog(@"松油门");
}

- (void)releaseAccelerator {
    NSLog(@"刹车");
}

@end

复制代码

计价器类Taximeter实现

@interface Taximeter : NSObject

- (void)start;
- (void)stop;

@end

@implementation Taximeter

- (void)start {
    NSLog(@"启动计价器");
}

- (void)stop {
    NSLog(@"停下计价器");
}
复制代码

外观类CabDriverdriveToLocation接口整合Taximeter Car子系统为外部提供接口

@interface CabDriver : NSObject

- (void)driveToLocation:(CGPoint)x;

@end


@implementation CabDriver

- (void)driveToLocation:(CGPoint)x {
    // 启动计价器
    Taximeter *meter = [[Taximeter alloc] init];
    [meter start];
    
    // 操作车辆,直到抵达位置
    Car *car = [[Car alloc] init];
    [car releaseBreaks];
    [car changeGears];
    [car pressAccelerator];
    
    // 当到达了位置x,就停下车和计价器
    [car releaseAccelerator];
    [car pressBreaks];
    [meter stop];
}

@end
复制代码
5.总结

外观有助于提供一种更为简洁的方式来使用子系统中的这些类.处理这些子系统类的默认行为,可能只是定义在外观的一个简便方法,而不必直接去使用这些类时便可以使用外观设计模式了.

对象去耦

中介者

1.概述

用一个对象来封装一系列对象的交互方式.中介者使对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互.

2.使用场景
  • 对象间的交互定义明确而非常复杂,导致一组对象彼此相互依赖而且难以理解.
  • 因为对象引用了许多其他对象并与其通讯,导致对象难以复用.
  • 想要定制一个分布在多个类中的逻辑或行为,又不想生成太多子类.
3.结构UML图

4.代码实例--实例间的信息通知

原文: Objective-C设计模式——中介者Mediator

中介者模式很好的诠释了迪米特法则,任意两个不相关的对象之间如果需要关联,那么需要通过第三个类来进行。中介者就是把一组对象进行封装,屏蔽了类之间的交互细节,使不同的类直接不需要持有对方引用也可以进行访问。

中介者Mediator会持有同事类(就是需要处理交互逻辑的对象)Colleague的引用,同时每个colleague也会持有Mediator一份引用。这样colleague如果有任何和别的类交互的请求就会发给Mediator,对改组对象进行了解耦合。其实我们平时经常写的视图控制器本身就是一个中介者,它来控制着不同对象之间的交互行为。

中介者Mediator

@interface Mediator : NSObject

@property (nonatomic, weak) ColleagueA *collA;
@property (nonatomic, weak) ColleagueB *collB;

- (void)notify:(id)obj;

@end

@implementation Mediator

- (void)notify:(id)obj {
    if (obj == self.collA) {
        [self.collB notified:@"B Notified"];
    } else {
        [self.collA notified:@"A Notified"];
    }
}

@end
复制代码

同事类基类Colleague

@interface Colleague : NSObject

@property (nonatomic, weak) Mediator *mediator;

- (instancetype)initWithMediator:(Mediator *)mediator;

- (void)notifyAnother;
- (void)notified:(NSString *)message;

@end

@implementation Colleague

- (instancetype)initWithMediator:(Mediator *)mediator {
    if (self = [super init]) {
        self.mediator = mediator;
    }
    return self;
}

- (void)notifyAnother {
    [self.mediator notify:self];
}

- (void)notified:(NSString *)message {
    NSLog(@"%@", message);
}

@end
复制代码

同事类AColleagueA

@interface ColleagueA : Colleague

@end

@implementation ColleagueA

@end
复制代码

设置同事类BColleagueB与同事类A一致

控制器实现

Mediator *mediator = [[Mediator alloc] init];
    ColleagueA *caA = [[ColleagueA alloc] initWithMediator:mediator];
    ColleagueB *caB = [[ColleagueB alloc] initWithMediator:mediator];
    
    mediator.collA = caA;
    mediator.collB = caB;
    
    [caA notifyAnother];
    [caB notifyAnother];
复制代码

结果展示

2019-04-18 23:27:55.290764+0800 MediatorPattern[86780:1876003] B Notified
2019-04-18 23:27:55.290933+0800 MediatorPattern[86780:1876003] A Notified
复制代码
5.总结

虽然对于处理应用程序的行为分散于不同对象并且对象相互依存的情况,中介者模式非常有用,但是应当注意避免让中介者过于庞大而难以维护.如果已经这样了,可以考虑使用另一种设计模式把它分解.要创造性地混用和组合各种设计模式解决同一个问题.

扩展: 组件化和模块化间用于各个模块的跳转交互所用的就是中介者模式.

观察者

1.概述
  • 观察者模式也叫发布-订阅模式.
  • 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.
2.使用场景
  • 有两种抽象类型相互依赖.将它们封装在各自的对象中,就可以对它们单独进行改变和复用.
  • 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变.
  • 一个对象必须通知其他对象,而它又不需要知道其他对象是什么.
3.结构UML图

4.代码实例--Cocoa Touch框架中使用的观察者模式--通知键-值观察

定义模型类Person

UIKIT_EXTERN NSNotificationName const PersonNameWillChangeNotification;

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

NSNotificationName const PersonNameWillChangeNotification = @"PersonNameWillChangeNotification";

@implementation Person

- (void)setName:(NSString *)name {
    _name = name;
    
    // 发送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:PersonNameWillChangeNotification object:_name];
}

@end
复制代码

控制器实现

对象赋初值

    _xiaoming = [[Person alloc] init];
    _xiaoming.name = @"xiaoming";
    _xiaoming.age = 11;
复制代码

通过通知监听名字更替

    // 通知更换了名字
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changePersonName:) name:PersonNameWillChangeNotification object:nil];
    
    _xiaoming.name = @"wangxiaoming";
    
    - (void)changePersonName:(NSNotification *)noti {
    NSLog(@"newName = %@", noti.object);
}

//2019-03-29 22:39:23.243012+0800 ObserverPattern[9755:1317800] newName = wangxiaoming
复制代码

通过键-值观察监听年龄变化

    [self.xiaoming addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

    _xiaoming.age = 28;

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"newAge = %ld", [change[NSKeyValueChangeNewKey] integerValue]);
    
    //2019-03-29 22:39:23.243229+0800 ObserverPattern[9755:1317800] newAge = 28

}
复制代码

具体实现

- (void)viewDidLoad {
    [super viewDidLoad];

    _xiaoming = [[Person alloc] init];
    _xiaoming.name = @"xiaoming";
    _xiaoming.age = 11;
    
    // 通知更换了名字
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changePersonName:) name:PersonNameWillChangeNotification object:nil];

    // 监听xiaoming年龄变化
    [self.xiaoming addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    
    _xiaoming.name = @"wangxiaoming";
    _xiaoming.age = 28;
    
    // 移除相关监听
    [[NSNotificationCenter defaultCenter] removeObserver:self name:PersonNameWillChangeNotification object:nil];
    [self.xiaoming removeObserver:self forKeyPath:@"age"];
}

- (void)changePersonName:(NSNotification *)noti {
    NSLog(@"newName = %@", noti.object);
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"newAge = %ld", [change[NSKeyValueChangeNewKey] integerValue]);
}
复制代码
5.总结

通知和KVOiOS中常用的数据交互传输的两种手段.特别是在需求需要双向绑定的情况下KVO是非常适合使用的.开发中可以使用KVO来实现类MVVM这样的框架.

抽象集合

组合

1.概述

将对象组合成树形结构以表示"部分-整体"的层次结构.组合使得用户对单个对象和组合对象的使用具有一致性.

2.使用场景
  • 想获得对象抽象的树形表示(部分-整体层次结构).
  • 想让客户端统一处理组合结构中的所有对象.
3.结构UML图

4.代码实例

原文: 组合模式(Composite)

定义基础组件类Components

@interface Components : NSObject

@property (nonatomic, copy) NSString *name;

- (instancetype)initWithName:(NSString *)name;
- (void)addComponent:(Components *)component;
- (void)removeComponent:(Components *)component;
- (void)display:(NSInteger)depth;

@end

@implementation Components

- (instancetype)initWithName:(NSString *)name {
    if (self = [super init]) {
        self.name = name;
    }
    return self;
}

- (void)addComponent:(Components *)component {
    return;
}

- (void)removeComponent:(Components *)component {
    return;
}

- (void)display:(NSInteger)depth {
    return;
}

@end

复制代码

定义叶子的子类Leaf

@interface Leaf : Components

@end

@implementation Leaf

- (void)addComponent:(Components *)component {
    NSLog(@"can not add");
}

- (void)removeComponent:(Components *)component {
    NSLog(@"can not remove");
}

- (void)display:(NSInteger)depth {
    NSLog(@"[level:%ld_%@]",depth, self.name);
}

@end
复制代码

定义节点类Composite,节点可包含新节点或新叶子

@interface Composite : Components

@property (nonatomic, strong) NSMutableArray *childArr;

@end

@implementation Composite

- (instancetype)initWithName:(NSString *)name {
    if (self = [super initWithName:name]) {
        self.childArr = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)addComponent:(Components *)component {
    [self.childArr addObject:component];
}

- (void)removeComponent:(Components *)component {
    [self.childArr removeObject:component];
}

- (void)display:(NSInteger)depth {
    NSLog(@"[level:%ld_%@]",depth, self.name);
    for (Components *component in self.childArr) {
        [component display:depth + 1];
    }
}

@end

复制代码

控制器实现

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Composite *root = [[Composite alloc] initWithName:@"root"];
    [root addComponent:[[Leaf alloc] initWithName:@"leaf-A"]];
    [root addComponent:[[Leaf alloc] initWithName:@"leaf-B"]];
   
    Composite *comp0 = [[Composite alloc] initWithName:@"comp0"];
    [comp0 addComponent:[[Leaf alloc] initWithName:@"comp0-A"]];
    [comp0 addComponent:[[Leaf alloc] initWithName:@"comp0-B"]];
    [root addComponent:comp0];
    
    Composite *comp1 = [[Composite alloc] initWithName:@"comp1"];
    [comp1 addComponent:[[Leaf alloc] initWithName:@"comp1-A"]];
    [comp1 addComponent:[[Leaf alloc] initWithName:@"comp1-B"]];
    [root addComponent:comp1];
    
    Composite *comp00 = [[Composite alloc] initWithName:@"comp00"];
    [comp00 addComponent:[[Leaf alloc] initWithName:@"comp00-A"]];
    [comp00 addComponent:[[Leaf alloc] initWithName:@"comp00-B"]];
    [comp0 addComponent:comp00];
    
    Leaf *leafC = [[Leaf alloc] initWithName:@"leaf-C"];
    [root addComponent:leafC];
    [root addComponent:[[Leaf alloc] initWithName:@"leaf-D"]];
    
    [root display:0];
    
    NSLog(@"-------- 删除leaf-C --------");

    // 删除leaf-C
    [root removeComponent:leafC];
    [root display:0];
    
    NSLog(@"-------- 删除comp0 --------");
    // 删除comp0
    [root removeComponent:comp0];
    [root display:0];
}


@end

复制代码

结果展示

[level:0_root]
[level:1_leaf-A]
[level:1_leaf-B]
[level:1_comp0]
[level:2_comp0-A]
[level:2_comp0-B]
[level:2_comp00]
[level:3_comp00-A]
[level:3_comp00-B]
[level:1_comp1]
[level:2_comp1-A]
[level:2_comp1-B]
[level:1_leaf-C]
[level:1_leaf-D]
-------- 删除leaf-C --------
[level:0_root]
[level:1_leaf-A]
[level:1_leaf-B]
[level:1_comp0]
[level:2_comp0-A]
[level:2_comp0-B]
[level:2_comp00]
[level:3_comp00-A]
[level:3_comp00-B]
[level:1_comp1]
[level:2_comp1-A]
[level:2_comp1-B]
[level:1_leaf-D]
-------- 删除comp0 --------
[level:0_root]
[level:1_leaf-A]
[level:1_leaf-B]
[level:1_comp1]
[level:2_comp1-A]
[level:2_comp1-B]
[level:1_leaf-D]
复制代码
5.总结

Cocoa Touch框架中,UIView被组织成一个组合结构.每个UIView的实例可以包含UIView的其他实例,形成统一的树形结构.让客户端对单个UIView对象和UIView的组合统一对待.

组合模式的主要意图是让树形结构中的每个节点具有相同的抽象接口.这样整个结构可作为一个统一的抽象结构使用,而不暴露其内部展示.对每个节点(叶节点或组合体)的任何操作,可以通过协议或抽象基类中定义的相同接口来进行.

迭代器

1.概述

提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示.

2.使用场景
  • 需要访问组合对象的内容,而又不暴露其内部表示.
  • 需要通过多种方式遍历组合对象.
  • 需要提供一个统一的接口,用来遍历各种类型的组合对象.
3.结构UML图

ListListIterator之间关系的类图

抽象列表与迭代器之间关系的类图

4.代码实例

参考:iOS设计模式--迭代器

4.1 系统迭代器
    // 系统迭代器
    NSArray *arr = @[@"A", @"B", @"C", @"D"];
    NSEnumerator *enumerator = [arr objectEnumerator];
    NSString *obj = nil;
    while ((obj = enumerator.nextObject)) {
        NSLog(@"%@", obj);
    }
复制代码
4.2 自定义迭代器

定义节点类Node

@interface Node : NSObject

/// 下一节点
@property (nonatomic, strong) Node *nextNode;
@property (nonatomic, strong) NSString *nodeName;

+ (instancetype)nodeWithName:(NSString *)name;

@end

@implementation Node

+ (instancetype)nodeWithName:(NSString *)name {
    Node *node = [[Node alloc] init];
    node.nodeName = name;
    return node;
}


@end

复制代码

定义链表类LinkedList

@interface LinkedList : NSObject

@property (nonatomic, strong, readonly) Node *firstNode;
@property (nonatomic, assign, readonly) NSInteger count;

- (void)addItem:(NSString *)item;

@end

@interface LinkedList ()

@property (nonatomic, strong) Node *firstNode;
@property (nonatomic, assign) NSInteger count;

@end

@implementation LinkedList

- (void)addItem:(NSString *)item {
    if (self.firstNode == nil) {
        self.firstNode = [Node nodeWithName:item];
        self.count++;
    } else {
        [self addItem:item node:self.firstNode];
    }
}

- (void)addItem:(NSString *)item node:(Node *)node {
    if (!node.nextNode) {
        node.nextNode = [Node nodeWithName:item];
        self.count++;
    } else {
        [self addItem:item node:node.nextNode];
    }
}

@end
复制代码

定义协议IteratorProtocol

@protocol IteratorProtocol <NSObject>

// 下一个元素
- (id)nextObject;

@end
复制代码

定义迭代器类LinkedListIterator

@interface LinkedListIterator : NSObject<IteratorProtocol>

+ (instancetype)linkedListIteratorWithLinkedList:(LinkedList *)linkedList;

@end

@interface LinkedListIterator ()

@property (nonatomic, strong) LinkedList *linkedList;
@property (nonatomic, strong) Node *currentNode;

@end

@implementation LinkedListIterator

+ (instancetype)linkedListIteratorWithLinkedList:(LinkedList *)linkedList {
    LinkedListIterator *iterator = [[LinkedListIterator alloc] init];
    iterator.linkedList = linkedList;
    return iterator;
}

- (id)nextObject {
    if (!self.currentNode) {
        self.currentNode = self.linkedList.firstNode;
    } else {
        self.currentNode = self.currentNode.nextNode;
    }
    return self.currentNode;
}

@end

复制代码

控制器实现

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 系统迭代器
    NSArray *arr = @[@"A", @"B", @"C", @"D"];
    NSEnumerator *enumerator = [arr objectEnumerator];
    NSString *obj = nil;
    while ((obj = enumerator.nextObject)) {
        NSLog(@"%@", obj);
    }
    
    NSLog(@"------------------------------------");
    
    LinkedList *list = [[LinkedList alloc] init];
    [list addItem:@"A"];
    [list addItem:@"B"];
    [list addItem:@"C"];
    [list addItem:@"D"];
    
    LinkedListIterator *iterator = [LinkedListIterator linkedListIteratorWithLinkedList:list];
    Node *node;
    while ((node = iterator.nextObject)) {
        NSLog(@"%@", node.nodeName);
    }
}


@end
复制代码

结果展示

2019-04-07 23:00:13.950111+0800 IteratorPattern[10885:90208] A
2019-04-07 23:00:13.950321+0800 IteratorPattern[10885:90208] B
2019-04-07 23:00:13.950442+0800 IteratorPattern[10885:90208] C
2019-04-07 23:00:13.950584+0800 IteratorPattern[10885:90208] D
2019-04-07 23:00:13.950706+0800 IteratorPattern[10885:90208] ------------------------------------
2019-04-07 23:00:13.950840+0800 IteratorPattern[10885:90208] A
2019-04-07 23:00:13.951111+0800 IteratorPattern[10885:90208] B
2019-04-07 23:00:13.951952+0800 IteratorPattern[10885:90208] C
2019-04-07 23:00:13.953016+0800 IteratorPattern[10885:90208] D
复制代码
5.总结

苹果公司用自己的命名规则"枚举器/枚举"改写了迭代器模式,用于相关基础类的各种方法.

基础框架中的NSEnumerator类实现了迭代器模式. NSArrayNSSetNSDictionary这样的集合类,定义了返回与集合的类型相应的NSEnumerator子类实例的方法.

迭代器模式访问者模式有些类似,尤其是把遍历算法放到访问者模式中或者在遍历聚合体时让内部迭代器对元素执行操作的时候.组合模式常常依靠迭代器来遍历其递归结构.多态的迭代器依靠工厂方法来实例化适当的迭代器具体子类.

行为扩展

访问者

1.概述

表示一个作用于某个对象结构中的各元素的操作.它让我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作.

2.使用场景
  • 一个复杂的对象结构包含很多其他对象,他们有不同的接口(比如组合体),但是想对这些对象实施一些依赖于其具体类型的操作.
  • 需要对一个组合结构中的对象进行很多不相关的操作,但是不想让这些操作"污染"这些对象的类.可以将相关的操作集中起来,定义在一个访问者类中,并在需要在访问者中定义的操作时使用它.
  • 定义复杂结构的类很少作修改,但经常需要向其添加新的操作.
3.结构UML图

4.代码实例--在屏幕上涂鸦,把手指滑动的轨迹绘制出来

协议类Mark

// 不论线条还是点,其实都是在介质上留下的标志(Mark),它为所有具体类定义了属性和方法
@protocol Mark <NSObject>

@property (nonatomic, assign) CGPoint location;
@property (nonatomic, assign) CGSize size;
@property (nonatomic, strong) id<Mark> lastChild;

- (void)addMark:(id<Mark>)mark;
- (void)removeMark:(id<Mark>)mark;

- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor;

@end
复制代码

组件类点Dot

// 点,组件只有一个点,那么它会表现为一个实心圆,在屏幕上代表一个点
@interface Dot : NSObject<Mark>

@end

@implementation Dot

@synthesize location;

@synthesize lastChild;

@synthesize size;

- (void)addMark:(id<Mark>)mark {}

- (void)removeMark:(id<Mark>)mark {}

- (id<Mark>)lastChild {
    return nil;
}

- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor {
    [visitor visitDot:self];
}

@end
复制代码

组件类顶点Vertex

// 顶点,连接起来的一串顶点,被绘制成连接起来的线条
@interface Vertex : NSObject<Mark>

@end

@implementation Vertex

@synthesize location;

@synthesize lastChild;

@synthesize size;

- (void)addMark:(id<Mark>)mark {}

- (void)removeMark:(id<Mark>)mark {}

- (id<Mark>)lastChild {
    return nil;
}

- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor {
    [visitor visitVertex:self];
}

@end
复制代码

组件类线条Stroke

@interface Stroke ()

@property (nonatomic, strong) NSMutableArray<id<Mark>> *children;

@end

@implementation Stroke

@dynamic location;

- (instancetype)init {
    if (self = [super init]) {
        _children = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)addMark:(id<Mark>)mark {
    [_children addObject:mark];
}

- (void)removeMark:(id<Mark>)mark {
    [_children removeObject:mark];
}

- (void)setLocation:(CGPoint)location {
    
}

- (CGPoint)location {
    if (_children.count == 0) {
        return CGPointZero;
    } else {
        return self.children.firstObject.location;
    }
}

- (id<Mark>)lastChild {
    return _children.lastObject;
}

- (void)acceptMarkVisitor:(id<MarkVisitor>)visitor {
    for (id<Mark> dot in self.children) {
        [dot acceptMarkVisitor:visitor];
    }
    
    [visitor visitStroke:self];
}

@end
复制代码

定义访问者接口类MarkVisitor

// 访问者接口
@protocol MarkVisitor <NSObject>

- (void)visitMark:(id<Mark>)mark;
- (void)visitVertex:(Vertex *)vertex;
- (void)visitDot:(Dot *)dot;
- (void)visitStroke:(Stroke *)stroke;

@end
复制代码

定义绘制类MarkRenderer

// 具体的访问者,MarkRenderer绘制访问者,它是对这些点和先进行绘制操作的
@interface MarkRenderer : NSObject<MarkVisitor>

- (instancetype)initWithCGContext:(CGContextRef)context;

@end

@interface MarkRenderer ()

@property (nonatomic, assign) CGContextRef context;
@property (nonatomic, assign) BOOL shouldMoveContextToDot;

@end

@implementation MarkRenderer

- (instancetype)initWithCGContext:(CGContextRef)context {
    if (self = [super init]) {
        self.context = context;
        self.shouldMoveContextToDot = YES;
    }
    return self;
}

- (void)visitMark:(id<Mark>)mark {
    
}

- (void)visitVertex:(Vertex *)vertex {
    CGFloat x = vertex.location.x;
    CGFloat y = vertex.location.y;
    if (self.shouldMoveContextToDot) {
        CGContextMoveToPoint(self.context, x, y);
        self.shouldMoveContextToDot = NO;
    } else {
        CGContextAddLineToPoint(self.context, x, y);
    }
}

- (void)visitDot:(Dot *)dot {
    CGFloat x = dot.location.x;
    CGFloat y = dot.location.y;
    CGRect frame = CGRectMake(x, y, 2, 2);
    
    CGContextSetFillColorWithColor(self.context, [[UIColor blackColor] CGColor]);
    CGContextFillEllipseInRect(self.context, frame);
}

- (void)visitStroke:(Stroke *)stroke {
    CGContextSetStrokeColorWithColor(self.context, [UIColor blueColor].CGColor);
    CGContextSetLineWidth(self.context, 1);
    CGContextSetLineCap(self.context, kCGLineCapRound);
    CGContextStrokePath(self.context);
    self.shouldMoveContextToDot = YES;
}

@end
复制代码

定义用于展示的视图CanvasView

@interface CanvasView : UIView

@property (nonatomic, strong) id<Mark> mark;

@end

@implementation CanvasView

- (void)setMark:(id<Mark>)mark {
    _mark = mark;
    
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    MarkRenderer *markRender = [[MarkRenderer alloc] initWithCGContext:context];
    [self.mark acceptMarkVisitor:markRender];
}

@end
复制代码

控制器实现

@interface ViewController ()

@property (nonatomic, strong) id<Mark> parentMark;
@property (nonatomic, strong) CanvasView *canvasView;
@property (nonatomic, assign) CGPoint startPoint;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.parentMark = [[Stroke alloc] init];
    [self setupView];
}

- (void)setupView
{
    CanvasView *canvasView = [[CanvasView alloc] initWithFrame:self.view.frame];
    canvasView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:canvasView];
    self.canvasView = canvasView;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.startPoint = [[touches anyObject] locationInView:self.canvasView];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CGPoint lastPoint = [[touches anyObject] previousLocationInView:self.canvasView];
    if (CGPointEqualToPoint(lastPoint, self.startPoint)) {
        id <Mark> newStroke = [[Stroke alloc] init];
        [self addMark:newStroke shouldAddToPreviousMark:NO];
    }
    
    CGPoint currentPoint  = [[touches anyObject] locationInView:self.canvasView];
    Vertex *vertex = [[Vertex alloc] init];
    vertex.location = currentPoint;
    [self addMark:vertex shouldAddToPreviousMark:vertex];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CGPoint lastPoint = [[touches anyObject] previousLocationInView:self.canvasView];
    CGPoint currentPoint = [[touches anyObject] locationInView:self.canvasView];
    if (CGPointEqualToPoint(lastPoint, currentPoint))
    {
        Dot *singleDot = [[Dot alloc] init];
        singleDot.location = currentPoint;
        [self addMark:singleDot shouldAddToPreviousMark:NO];
    }
    self.startPoint = CGPointZero;
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.startPoint = CGPointZero;
}

- (void)addMark:(id<Mark>)mark shouldAddToPreviousMark:(BOOL)shouldAddToPreviousMark
{
    if (shouldAddToPreviousMark) {
        [self.parentMark.lastChild addMark:mark];
    } else {
        [self.parentMark addMark:mark];
    }
    
    self.canvasView.mark = self.parentMark;
}

@end
复制代码
5.总结

访问者模式是扩展组合结构功能的一种强有力的方式.如果组合结构具有精心设计的基本操作,而且结构将来也不会变更,就可以使用访问者模式,用各种不同用途的访问者,以同样的方式访问这个组合结构.访问者模式用尽可能少的修改,可以把组合结构与其他访问者类中的相关算法分离.

装饰

1.概述
  • 持有对象的引用,不改变原始类,动态拓展对象的功能,不改变使用继承的情形
  • 动态地给一个对象添加一些额外的的职责.就扩展功能来说,装饰模式相比生成子类更为灵活.
2.使用场景
  • 想要在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责.
  • 想要扩展一个类的行为,却做不到.类定义可能被隐藏,无法进行子类化;或者,对类的每个行为的扩展,为支持每种功能组合,将产生大量的子类.
  • 对类的职责的扩展是可选的.
3.结构UML图

4.代码实例--为图片添加滤镜

普通实现

设置协议的抽象(我们想让所有的ImageComponent都能支持UIImage的重画方法)

@protocol ImageComponent <NSObject>

@optional

- (void)drawAtPoint:(CGPoint)point;
- (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)drawInRect:(CGRect)rect;
- (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
- (void)drawAsPatternInRect:(CGRect)rect;

@end
复制代码

设置核心的装饰类,也是滤镜基类

@interface ImageFilter : NSObject <ImageComponent>
{
    @private
    id <ImageComponent> component_;
}

/// 保持一个ImageComponent的引用,这个引用会被其他具体的装饰器装饰.
@property (nonatomic, strong) id<ImageComponent> component;

- (void)apply;
- (id)initWithImageComponent:(id<ImageComponent>)component;
- (id)forwardingTargetForSelector:(SEL)aSelector;
复制代码
- (id)initWithImageComponent:(id<ImageComponent>)component {
    if (self = [super init]) {
        [self setComponent:component];
    }
    
    return self;
}

- (void)apply {
    // 应该由子类重载,应用真正的滤镜
}

/// 截获消息
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSString *selectorName = NSStringFromSelector(aSelector);
    if ([selectorName hasPrefix:@"draw"]) {
        [self apply];
    }
    
    return component_;
}
复制代码

设置仿射变换滤镜


- (instancetype)initWithImageComponent:(id<ImageComponent>)component
                             transform:(CGAffineTransform)transform {
    if (self = [super initWithImageComponent:component]) {
        [self setTransform:transform];
    }
    
    return self;
}

- (void)apply {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextConcatCTM(context, transform_);
}

复制代码

设置阴影滤镜

- (void)apply {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGSize offset = CGSizeMake(-25, 15);
    CGContextSetShadow(context, offset, 20.0);
}
复制代码

视图展示配置

- (void)setImage:(UIImage *)image {
    image_ = image;
}

- (void)drawRect:(CGRect)rect {
    [image_ drawInRect:rect];
}
复制代码

控制器配置

UIImage *img = [UIImage imageNamed:@"13.jpeg"];
    CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
    CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(-img.size.width / 2.0, img.size.height / 8.0);
    CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform, translateTransform);
    
    id<ImageComponent>transformedImage = [[ImageTransformFilter alloc] initWithImageComponent:img transform:finalTransform];
    id<ImageComponent>finalImage = [[ImageShadowFilter alloc] initWithImageComponent:transformedImage];
    
    DecoratorView *decoratorView = [[DecoratorView alloc] initWithFrame:self.view.bounds];
    [decoratorView setImage:(UIImage *)finalImage];
    [self.view addSubview:decoratorView];
复制代码
  • iOS特有实现

设置基础滤镜配置方法

@interface UIImage (BaseFilter)

- (CGContextRef)beginContext;
- (UIImage *)getImageFromCurrentImageContext;
- (void)endContext;

@end
复制代码
@implementation UIImage (BaseFilter)

- (CGContextRef)beginContext {
    CGSize size = [self size];
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    return context;
}

- (UIImage *)getImageFromCurrentImageContext {
    [self drawAtPoint:CGPointZero];
    
    UIImage *imageOut = UIGraphicsGetImageFromCurrentImageContext();
    return imageOut;
}

- (void)endContext {
    UIGraphicsEndImageContext();
}

@end
复制代码

设置仿射变换滤镜方法

@implementation UIImage (Transform)

- (UIImage *)imageWithTransform:(CGAffineTransform)transform {
    CGContextRef context = [self beginContext];
    CGContextConcatCTM(context, transform);
    UIImage *imageOut = [self getImageFromCurrentImageContext];
    [self endContext];
    return imageOut;
}

@end
复制代码

设置阴影滤镜方法

@implementation UIImage (Shadow)

- (UIImage *)imageWithDropShadow {
    CGContextRef context = [self beginContext];
    CGSize offset = CGSizeMake(-25, 15);
    CGContextSetShadow(context, offset, 20.0);
    
    UIImage *imageOut = [self getImageFromCurrentImageContext];
    [self endContext];
    return imageOut;
}

@end
复制代码

控制器配置

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIImage *img = [UIImage imageNamed:@"13.jpeg"];
    CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
    CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(-img.size.width / 2.0, img.size.height / 8.0);
    CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform, translateTransform);
    
    UIImage *transformedImage = [img imageWithTransform:finalTransform];
    UIImage *finalImage = [transformedImage imageWithDropShadow];

    DecoratorView *decoratorView = [[DecoratorView alloc] initWithFrame:self.view.bounds];
    [decoratorView setImage:(UIImage *)finalImage];
    [self.view addSubview:decoratorView];
}
复制代码
5.总结

优点:

  • 把类中的装饰功能从类中搬移去除,这样可以简化原有的类
  • 有效地把类的核心职责和装饰功能区分开了.而且可以去除相关类中重复的装饰逻辑.

装饰模式在OC中会有不同的实现方式即为范畴.真正子类方式的实际使用是一种较为结构化的方式连接各种装饰器.范畴的方式更为简单和轻量,适用于现有类只需要少量装饰器的应用程序.

责任链

1.概述

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间发生耦合.此模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止.

2.使用场景
  • 有多个对象可以处理请求,而处理请求只有在运行时才能确定.
  • 向一组对象发出请求,而不想显式指定处理请求的特定处理程序.
3.结构UML图

责任链类图

请求处理典型结构图

4.代码实例--对文本进行判断.

原文:iOS-Design-Patterns

接口处理请求的协议ChainOfResponsibilityProtocol

@protocol ChainOfResponsibilityProtocol <NSObject>

@property (nonatomic, strong) id<ChainOfResponsibilityProtocol> successor;

- (void)handlerRequest:(id)request;

@end
复制代码

定义链头类HeadChain

/// 链头
@interface HeadChain : NSObject<ChainOfResponsibilityProtocol>

@end

@interface HeadChain ()

@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;

@end

@implementation HeadChain

@synthesize successor;

- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
    self.nextSuccessor = successor;
}

- (id<ChainOfResponsibilityProtocol>)successor {
    return self.nextSuccessor;
}

- (void)handlerRequest:(id)request {
    [self.successor handlerRequest:request];
}

@end
复制代码

定义验证手机号码节点类PhoneNumChain

/// 验证手机号码节点
@interface PhoneNumChain : NSObject<ChainOfResponsibilityProtocol>

@end

@interface PhoneNumChain ()

@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;

@end

@implementation PhoneNumChain

@synthesize successor;

- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
    self.nextSuccessor = successor;
}

- (id<ChainOfResponsibilityProtocol>)successor {
    return self.nextSuccessor;
}

- (void)handlerRequest:(id)request {
    NSString *str = request;
    if ([str isKindOfClass:[NSString class]] && str.length > 0) {
        // 匹配电话号码(手机号以13, 15,18开头,八个 \d 数字字符)
        BOOL isMatch = [str isMatch:RX(@"^((13[0-9])|(15[^4,\\D])|(18[0,0-9]))\\d{8}$")];
        if (isMatch) {
            NSLog(@"%@ is a PhoneNum", str);
        } else {
            [self.successor handlerRequest:request];
        }
    } else {
        [self.successor handlerRequest:request];
    }
}

@end
复制代码

验证邮箱节点类EmailChain

/// 验证邮箱节点
@interface EmailChain : NSObject<ChainOfResponsibilityProtocol>

@end

@interface EmailChain ()

@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;

@end

@implementation EmailChain

@synthesize successor;

- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
    self.nextSuccessor = successor;
}

- (id<ChainOfResponsibilityProtocol>)successor {
    return self.nextSuccessor;
}

- (void)handlerRequest:(id)request {
    NSString *str = request;
    if ([str isKindOfClass:[NSString class]] && str.length > 0) {
        // 匹配邮箱
        BOOL isMatch = [str isMatch:RX(@"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}")];
        if (isMatch) {
            NSLog(@"%@ is a Email", str);
        } else {
            [self.successor handlerRequest:request];
        }
    } else {
        [self.successor handlerRequest:request];
    }
}

@end
复制代码

验证用户名节点类UserNameChain

/// 验证用户名节点
@interface UserNameChain : NSObject<ChainOfResponsibilityProtocol>

@end

@interface UserNameChain ()

@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;

@end

@implementation UserNameChain

@synthesize successor;

- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
    self.nextSuccessor = successor;
}

- (id<ChainOfResponsibilityProtocol>)successor {
    return self.nextSuccessor;
}

- (void)handlerRequest:(id)request {
    NSString *str = request;
    if ([str isKindOfClass:[NSString class]] && str.length > 0) {
        // 匹配用户名(用户名长度为6-20位之间,大小写字母或者数字均可)
        BOOL isMatch = [str isMatch:RX(@"^[A-Za-z0-9]{6,20}+$")];
        if (isMatch) {
            NSLog(@"%@ is a UserName", str);
        } else {
            [self.successor handlerRequest:request];
        }
    } else {
        [self.successor handlerRequest:request];
    }
}

@end
复制代码

链尾类EndChain

/// 链尾
@interface EndChain : NSObject<ChainOfResponsibilityProtocol>

@end

@interface EndChain ()

@property (nonatomic, weak) id<ChainOfResponsibilityProtocol> nextSuccessor;

@end

@implementation EndChain

@synthesize successor;

- (void)setSuccessor:(id<ChainOfResponsibilityProtocol>)successor {
}

- (id<ChainOfResponsibilityProtocol>)successor {
    return self;
}

- (void)handlerRequest:(id)request {
    NSLog(@"无法处理该请求");
}

@end

复制代码

控制器实现

// 设置各请求链条
    HeadChain *headChain = [[HeadChain alloc] init];
    PhoneNumChain *phoneNumChain = [[PhoneNumChain alloc] init];
    EmailChain *emailChain = [[EmailChain alloc] init];
    UserNameChain *userNameChain = [[UserNameChain alloc] init];
    EndChain *endChain = [[EndChain alloc] init];
    
    // 设置下一链接点
    headChain.successor = phoneNumChain;
    phoneNumChain.successor = emailChain;
    emailChain.successor = userNameChain;
    userNameChain.successor = endChain;
    
    // 处理事件
    [headChain handlerRequest:@"18659007343"];
    [headChain handlerRequest:@"18659007343@qq.com"];
    [headChain handlerRequest:@"abc1865900"];
    [headChain handlerRequest:@"====="];
复制代码

结果展示

2019-04-17 00:26:21.661847+0800 ChainOfResponsibilityPattern[84339:1697824] 18659007343 is a PhoneNum
2019-04-17 00:26:21.662235+0800 ChainOfResponsibilityPattern[84339:1697824] 18659007343@qq.com is a Email
2019-04-17 00:26:21.662562+0800 ChainOfResponsibilityPattern[84339:1697824] abc1865900 is a UserName
2019-04-17 00:26:21.662889+0800 ChainOfResponsibilityPattern[84339:1697824] 无法处理该请求
复制代码
5.总结

CocoaUIResponder类就是责任链的一个实例.当操作应用时,进行点击,点击操作通过责任链一步步走下去直到找到相对应要响应的UIResponder.

算法封装

模板方法

1.概述

定义一个操作中算法的骨架,而将一些步骤延迟到子类中.模板方法使子类可以冲定义算法的某些特定步骤而不改变该算法的结构.

2.使用场景
  • 需要一次性实现算法的不同部分,并将可变的行为留给子类来实现.
  • 子类的共同行为应该被提取出来放到公共类中,以避免代码重复.现有代码的差别应该被分离为新的操作.然后用一个调用这些新操作的模板方法来替换这些不同的代码.
  • 需要控制子类的扩展.可以定义一个在特定点调用"钩子"操作的模板方法.子类可以通过对钩子操作的实现在这些点扩展功能.
3.结构UML图

4.代码实例--利用模板方法制作三明治

定义基础类AnySandwich

@interface AnySandwich : NSObject

- (void)make;
- (void)prepareBread;
- (void)putBreadOnPlate;
- (void)addMeat;
- (void)addCondiments;
- (void)serve;
- (void)extraStep;

@end
复制代码

内部实现制作三明治的基本流程. extraStep为钩子方法,子类可实现也可以不用实现. prepareBread addMeat addCondiments的具体实现交由子类.如若没有实现,将libc++abi.dylib: terminating with uncaught exception of type NSException运行失败来限制子类.

@implementation AnySandwich

- (void)make {
    [self prepareBread];
    [self putBreadOnPlate];
    [self addMeat];
    [self addCondiments];
    [self serve];
    [self extraStep];
}

- (void)putBreadOnPlate {
    // 做任何三明治都要先把面包放在盘子上
}

- (void)serve {
    // 任何三明治做好了都要上餐
}

#pragma mark -
#pragma Detail will be handled by subclasses

- (void)prepareBread {
    // 要保证子类重载这个方法
    [NSException raise:NSInternalInconsistencyException
                format:@"You must override%@in a subclass", NSStringFromSelector(_cmd)];
}

- (void)addMeat {
    // 要保证子类重载这个方法
    [NSException raise:NSInternalInconsistencyException
                format:@"You must override%@in a subclass", NSStringFromSelector(_cmd)];
}

- (void)addCondiments {
    // 要保证子类重载这个方法
    [NSException raise:NSInternalInconsistencyException
                format:@"You must override%@in a subclass", NSStringFromSelector(_cmd)];
}

@end

复制代码

ReubenSandwich实现

@implementation ReubenSandwich

- (void)prepareBread {
    [self cutRyeBread];
}

- (void)addMeat {
    [self addCornBeef];
}

- (void)addCondiments {
    [self addSauerkraut];
    [self addThousandIslandDressing];
    [self addSwissCheese];
}

- (void)extraStep {
    [self grillIt];
}

#pragma mark -
#pragma mark ReubenSandwich Specific Methods

- (void)cutRyeBread {
    // 鲁宾三明治需要两片黑麦面包
}

- (void)addCornBeef {
    // 鲁宾三明治加大量腌牛肉
}

- (void)addSauerkraut {
    // 加入德国酸菜
}

- (void)addThousandIslandDressing {
    // 加入千岛酱
}

- (void)addSwissCheese {
    // 加入上等瑞士奶酪
}

- (void)grillIt {
    // 最后要把它烤一下
}

@end
复制代码
5.总结

模板方法是代码复用的基本技术,是抽出共同行为放入框架类中的手段.这一方式有助于提高可扩展性与可复用性,而维持各种类之间的松耦合.

策略

1.概述
  • 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换.
  • 本模式使得算法可独立于使用它的客户而变化.
  • MVC:控制器与视图之间是一种基于策略模式的关系.
2.使用场景
  • 一个类在其操作中使用多个条件语句来定义许多行为.我们可以把相关的条件分支移到它们自己的策略类中.
  • 需要算法的各种变体.
  • 需要避免把复杂的、与算法相关的数据结构暴露给客户端.
  • 如果代码有很多条件语句,就可能意味着需要把它们重构成各种策略对象.
3.结构UML图

4.代码实例--验证字符串实现方式

创建算法抽象基类,开放验证接口并实现

@interface StringValidator : NSObject

- (BOOL)validateString:(NSString *)str error:(NSError **)error;

@end


@implementation StringValidator

- (BOOL)validateString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error {
    if (error) {
        *error = nil;
    }
    return NO;
}

@end
复制代码

创建相关的算法继承类 NumbericValidator数值判断类

@interface NumbericValidator : StringValidator

- (BOOL)validateString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error;

@end

@implementation NumbericValidator

- (BOOL)validateString:(NSString *)str error:(NSError *__autoreleasing  _Nullable *)error {
    NSError *regError = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[0-9]*$" options:NSRegularExpressionAnchorsMatchLines error:&regError];
    NSUInteger numberOfMatchs = [regex numberOfMatchesInString:str options:NSMatchingAnchored range:NSMakeRange(0, str.length)];
    if (numberOfMatchs == 0) {
        if (error != nil) {
            NSString *description = NSLocalizedString(@"Failed", @"");
            NSString *reason = NSLocalizedString(@"can contain only numberical", @"");
            NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
            NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, nil];
            NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
            *error = [NSError errorWithDomain:@"StringErrorDomain" code:1001 userInfo:userInfo];
        }
        return NO;
    }
    return YES;
}

@end
复制代码

AlphaValidator字母判断类

@interface AlphaValidator : StringValidator

- (BOOL)validateString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error;

@end

@implementation AlphaValidator

- (BOOL)validateString:(NSString *)str error:(NSError *__autoreleasing  _Nullable *)error {
    NSError *regError = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z]*$" options:NSRegularExpressionAnchorsMatchLines error:&regError];
    NSUInteger numberOfMatchs = [regex numberOfMatchesInString:str options:NSMatchingAnchored range:NSMakeRange(0, str.length)];
    if (numberOfMatchs == 0) {
        if (error != nil) {
            NSString *description = NSLocalizedString(@"Failed", @"");
            NSString *reason = NSLocalizedString(@"can contain only alpha", @"");
            NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
            NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, nil];
            NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
            *error = [NSError errorWithDomain:@"StringErrorDomain" code:1002 userInfo:userInfo];
        }
        return NO;
    }
    return YES;
}
@end

复制代码

算法策略类StringValidatorManager

@interface StringValidatorManager : NSObject

+ (BOOL)numbervalidString:(NSString *)str error:(NSError **)error;
+ (BOOL)alphaString:(NSString *)str error:(NSError **)error;

@end


@implementation StringValidatorManager

+ (BOOL)numbervalidString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error {
    NumbericValidator *number = [[NumbericValidator alloc] init];
    return [number validateString:str error:error];
}

+ (BOOL)alphaString:(NSString *)str error:(NSError * _Nullable __autoreleasing *)error {
    AlphaValidator *alpha = [[AlphaValidator alloc] init];
    return [alpha validateString:str error:error];
}

@end
复制代码

控制器实现

 NSString *test1 = @"112";
    NSError *error0;
    [StringValidatorManager numbervalidString:test1 error:&error0];
    if (error0) {
        NSLog(@"0: %@", error0.localizedFailureReason);
    }
    
    NSError *error1;
    [StringValidatorManager alphaString:test1 error:&error1];
    if (error1) {
        NSLog(@"1: %@", error1.localizedFailureReason);
    }
    
    NSString *test2 = @"adsf";
    NSError *error2;
    [StringValidatorManager numbervalidString:test2 error:&error2];
    if (error2) {
        NSLog(@"2: %@", error2.localizedFailureReason);
    }
    
    NSError *error3;
    [StringValidatorManager alphaString:test2 error:&error3];
    if (error3) {
        NSLog(@"3: %@", error3.localizedFailureReason);
    }
复制代码

结果展示

2018-12-25 22:22:45.027745+0800 StrategyPattern[21641:845119] 1: can contain only alpha
2018-12-25 22:22:45.028076+0800 StrategyPattern[21641:845119] 2: can contain only numberical
复制代码
5.总结

优点

  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为.
  • 策略模式提供了管理相关的算法族的办法.
  • 策略模式提供了可以替换继承关系的办法.
  • 使用策略模式可以避免使用多重条件转移语句.

策略模式装饰模式有些相似.装饰器从外部扩展对象的行为,而各种策略则被封装在对象之中.所以说装饰器改变对象的"外表"而策略改变对象的"内容".

命令

1.概述

将请求封装成一个对象,从而可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作.

2.使用场景
  • 想让应用程序支持撤销与恢复.
  • 想用对象参数化一个动作以执行操作,并用不同命令对象来代替回调函数.
  • 想要在不同时刻对请求进行指定、排列和执行.
  • 想记录修改日志,这样在系统故障时,这些修改可在后来重做一遍.
  • 想让系统支持事务,事务封装了对数据的一系列修改.事务可以建模为命令对象.
3.结构UML图

4.代码实例--发送命令控制灯和CD播放器的开关.

原文:设计模式系列 6-- 命令模式

Client创建ConcreteCommand对象并设定器receiver; Invoker要求通用命令(实际上是ConcreteCommand)实施请求; Command要为Invoker所知的通用接口; ConcreteCommandReceiver和对它的操作action之间的中间人作用; Receiver可以随着由Command(ConcreteCommand)对象实施的相应请求,而执行实际操作的任何对象.

接口协议CommandInterface

/// 命令对象的公共接口(按钮执行的动作)
@protocol CommandInterface <NSObject>

/// 执行命令
- (void)execute;

/// 撤销
- (void)undo;

@end
复制代码

定义灯类Light

/// 电灯类
@interface Light : NSObject

- (void)lightOn;
- (void)lightOff;

@end

@implementation Light

- (void)lightOn {
    NSLog(@"开灯");
}

- (void)lightOff {
    NSLog(@"关灯");
}

@end
复制代码

定义CD播放器类CDPlayer

/// CD播放器类
@interface CDPlayer : NSObject

- (void)CDPlayerOn;
- (void)CDPlayerOff;

@end

@implementation CDPlayer

- (void)CDPlayerOn {
    NSLog(@"打开CD播放器");
}

- (void)CDPlayerOff {
    NSLog(@"关闭CD播放器");
}

@end
复制代码

打开灯命令类LightOnCommand

/// 打开灯命令类
@interface LightOnCommand : NSObject<CommandInterface>

- (instancetype)initWithLight:(Light *)light;

@end

@implementation LightOnCommand

- (instancetype)initWithLight:(Light *)light {
    if (self = [super init]) {
        self.light = light;
    }
    return self;
}

- (void)execute {
    [self.light lightOn];
}

- (void)undo {
    [self.light lightOff];
}

@end
复制代码

关灯命令类LightOffCommand

/// 关灯命令类
@interface LightOffCommand : NSObject<CommandInterface>

- (instancetype)initWithLight:(Light *)light;

@end

@implementation LightOffCommand

- (instancetype)initWithLight:(Light *)light {
    if (self = [super init]) {
        self.light = light;
    }
    return self;
}

- (void)execute {
    [self.light lightOff];
}

- (void)undo {
    [self.light lightOn];
}

@end
复制代码

CD播放器播放类CDPlayerOnCommand

/// CD播放器播放类
@interface CDPlayerOnCommand : NSObject<CommandInterface>

- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer;

@end

@implementation CDPlayerOnCommand

- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer {
    if (self = [super init]) {
        self.cdPlayer = cdPlayer;
    }
    return self;
}

- (void)execute {
    [self.cdPlayer CDPlayerOn];
}

- (void)undo {
    [self.cdPlayer CDPlayerOff];
}

@end
复制代码

定义CD播放器关闭类CDPlayerOffCommand

/// CD播放器关闭类
@interface CDPlayerOffCommand : NSObject<CommandInterface>

- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer;

@end

@implementation CDPlayerOffCommand

- (instancetype)initWithCDPlayer:(CDPlayer *)cdPlayer {
    if (self = [super init]) {
        self.cdPlayer = cdPlayer;
    }
    return self;
}

- (void)execute {
    [self.cdPlayer CDPlayerOff];
}

- (void)undo {
    [self.cdPlayer CDPlayerOn];
}

@end
复制代码

定义遥控器类RemoteControl

/// 命令调用者(遥控器)
@interface RemoteControl : NSObject

//@property (nonatomic, strong) id<CommandInterface> slot;

- (void)onClickWithIdx:(NSInteger)idx;
- (void)offClickWithIdx:(NSInteger)idx;

- (void)setCommandWithIdx:(NSInteger)idx
                onCommand:(id<CommandInterface>)onCommand
               offCommand:(id<CommandInterface>)offCommand;

/// 撤销刚才的操作
- (void)undoAction;

/// 撤销所有操作
- (void)undoAllAction;

@end

@interface RemoteControl ()

@property (nonatomic, strong) NSArray<id<CommandInterface>> *onCommands;
@property (nonatomic, strong) NSArray<id<CommandInterface>> *offCommands;
@property (nonatomic, strong) id<CommandInterface> undoCommand;   ///< 上一次的命令
@property (nonatomic, strong) NSMutableArray<id<CommandInterface>> *completeCommandsArr;
@end

@implementation RemoteControl

- (instancetype)init {
    if (self = [super init]) {
        // 默认有4类命令类型
        NSMutableArray *mOnArr = [[NSMutableArray alloc] init];
        NSMutableArray *mOffArr = [[NSMutableArray alloc] init];
        for (int i = 0; i < 4; ++i) {
            [mOnArr addObject:[[DefaultCommand alloc] init]];
            [mOffArr addObject:[[DefaultCommand alloc] init]];
        }
        self.onCommands = mOnArr.copy;
        self.offCommands = mOnArr.copy;
        self.completeCommandsArr = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)onClickWithIdx:(NSInteger)idx {
    if (idx >= self.onCommands.count || idx < 0) {
        return;
    }
    
    [self.onCommands[idx] execute];
    self.undoCommand = self.onCommands[idx];
    [self.completeCommandsArr addObject:self.onCommands[idx]];
}

- (void)offClickWithIdx:(NSInteger)idx {
    if (idx >= self.offCommands.count || idx < 0) {
        return;
    }
    
    [self.offCommands[idx] execute];
    self.undoCommand = self.offCommands[idx];
    [self.completeCommandsArr addObject:self.offCommands[idx]];
}

- (void)undoAction {
    [self.undoCommand undo];
    [self.completeCommandsArr removeObject:self.undoCommand];
}

- (void)undoAllAction {
    [self.completeCommandsArr enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id<CommandInterface>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj undo];
    }];
    
    [self.completeCommandsArr removeAllObjects];
}

- (void)setCommandWithIdx:(NSInteger)idx
                onCommand:(id<CommandInterface>)onCommand
               offCommand:(id<CommandInterface>)offCommand {
    if (idx < 0 || idx >= self.onCommands.count) {
        return;
    }
    
    if (idx < 0 || idx >= self.offCommands.count) {
        return;
    }
    
    NSMutableArray *mOnCommands = [self.onCommands mutableCopy];
    [mOnCommands replaceObjectAtIndex:idx withObject:onCommand];
    self.onCommands = mOnCommands;
    
    NSMutableArray *mOffCommands = [self.offCommands mutableCopy];
    [mOffCommands replaceObjectAtIndex:idx withObject:offCommand];
    self.offCommands = mOffCommands;
}

@end

复制代码

定义初始的命令类

/// 默认命令类(设备默认状态)
@interface DefaultCommand : NSObject<CommandInterface>

@end

@implementation DefaultCommand

- (void)execute {
    NSLog(@"默认命令状态下");
}

- (void)undo {
    NSLog(@"默认撤销");
}

@end

复制代码

定义面板用于提供给外接接口

/// 命令装配者(将命令安装到遥控器上)
@interface RemoteLoader : NSObject

@property (nonatomic, strong, readonly) RemoteControl *rc;

- (instancetype)initWithRemoteControl:(RemoteControl *)rc;

@end

@implementation RemoteLoader

- (instancetype)initWithRemoteControl:(RemoteControl *)rc {
    if (self = [super init]) {
        self.rc = rc;
        [self configCommands];
    }
    return self;
}

- (void)configCommands {
    Light *light = [[Light alloc] init];
    LightOnCommand *lightOnCommand = [[LightOnCommand alloc] initWithLight:light];
    LightOffCommand *lightOffCommand = [[LightOffCommand alloc] initWithLight:light];
    [self.rc setCommandWithIdx:0 onCommand:lightOnCommand offCommand:lightOffCommand];
    
    CDPlayer *cd = [[CDPlayer alloc] init];
    CDPlayerOnCommand *cdPlayerOnCommand = [[CDPlayerOnCommand alloc] initWithCDPlayer:cd];
    CDPlayerOffCommand *cdPlayerOffCommand = [[CDPlayerOffCommand alloc] initWithCDPlayer:cd];
    [self.rc setCommandWithIdx:1 onCommand:cdPlayerOnCommand offCommand:cdPlayerOffCommand];
}

@end

复制代码

控制器实现

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    RemoteControl *rc = [[RemoteControl alloc] init];
    RemoteLoader *loader = [[RemoteLoader alloc] initWithRemoteControl:rc];
    [loader.rc onClickWithIdx:0];
    [loader.rc onClickWithIdx:1];
    [loader.rc offClickWithIdx:0];
    [loader.rc offClickWithIdx:1];
    
    NSLog(@"------------执行撤销动作------------");
    [loader.rc undoAction];
    [loader.rc undoAllAction];
}

@end
复制代码

结果展示

2019-04-14 21:54:05.762810+0800 CommandPattern[68418:1486836] 开灯
2019-04-14 21:54:05.762981+0800 CommandPattern[68418:1486836] 打开CD播放器
2019-04-14 21:54:05.763088+0800 CommandPattern[68418:1486836] 关灯
2019-04-14 21:54:05.763198+0800 CommandPattern[68418:1486836] 关闭CD播放器
2019-04-14 21:54:05.763315+0800 CommandPattern[68418:1486836] ------------执行撤销动作------------
2019-04-14 21:54:05.763417+0800 CommandPattern[68418:1486836] 打开CD播放器
2019-04-14 21:54:05.763661+0800 CommandPattern[68418:1486836] 开灯
2019-04-14 21:54:05.763770+0800 CommandPattern[68418:1486836] 关闭CD播放器
2019-04-14 21:54:05.763886+0800 CommandPattern[68418:1486836] 关灯

复制代码
5.总结

Cocoa Touch框架中使用命令模式的典型例子.

NSInvocation

NSUndoManager

性能与对象访问

享元

1.概述

运用共享技术有效地支持大量细粒度的对象.

2.使用场景
  • 应用程序使用很多对象.
  • 在内存中保存对象会影响内存性能.
  • 对象的多数特有状态(外在状态)可以放到外部而轻量化.
  • 移除了外在状态之后,可以用较少的共享对象替代原来的那组对象.
  • 应用程序不依赖于对象标识,因为共享对象不能提供唯一的标识.
3.结构UML图

4.代码实例--绘制几百个花朵图案.

原文:Objective-C设计模式解析-享元

花朵视图FlowerView

// 绘制一朵花朵图案
@interface FlowerView : UIImageView

@end

@implementation FlowerView

- (void)drawRect:(CGRect)rect {
    [self.image drawInRect:rect];
}

@end
复制代码

花朵生成工厂类FlowerFactory.

`FlowerFactory`用`flowerPool`聚合了一个花朵池的引用.
`flowerPool`是一个保存`FlowerView`的所有实例的数据结构.
`FlowerFactory`通过`flowerViewWithType:`方法返回`FlowerView`
实例.
复制代码
static NSInteger kTotalNumberOfFlowTypes = 7;

typedef NS_ENUM(NSInteger, FlowerType) {
    kAnemone = 0,
    kCosmos,
    kGerberas,
    kHollyhock,
    kJasmine,
    kZinnia
};

NS_ASSUME_NONNULL_BEGIN

@interface FlowerFactory : NSObject

- (UIView *)flowerViewWithType:(FlowerType)type;

@end

NS_ASSUME_NONNULL_END

@interface FlowerFactory ()

@property (nonatomic, strong) NSMutableDictionary *flowerPool;

@end

@implementation FlowerFactory

- (UIView *)flowerViewWithType:(FlowerType)type {
    UIView *flowerView = [_flowerPool objectForKey:@(type)];
    if (flowerView) {
        return flowerView;
    }
    
    NSString *imgName;
    switch (type) {
        case kAnemone:
            imgName = @"anemone";
            break;
        case kCosmos:
            imgName = @"cosmos";
            break;
        case kGerberas:
            imgName = @"gerberas";
            break;
        case kHollyhock:
            imgName = @"hollyhock";
            break;
        case kJasmine:
            imgName = @"jasmine";
            break;
        case kZinnia:
            imgName = @"zinnia";
            break;
        default:
            break;
    }
    
    UIImage *img = [UIImage imageNamed:imgName];
    if (!img) {
        return nil;
    }
    
    FlowerView *tmpView = [[FlowerView alloc] init];
    tmpView.image = img;
    [self.flowerPool setObject:tmpView forKey:@(type)];
    return tmpView;
}

- (NSMutableDictionary *)flowerPool {
    if (!_flowerPool) {
        NSMutableDictionary *mDic = [[NSMutableDictionary alloc] initWithCapacity:kTotalNumberOfFlowTypes];
        
        _flowerPool = mDic;
    }
    return _flowerPool;
}

@end
复制代码

花朵外部数据结构ExtrinsicFlowerState

#ifndef ExtrinsicFlowerState_h
#define ExtrinsicFlowerState_h

struct ExtrinsicFlowerState {
    __unsafe_unretained UIView *flowerView;
    CGRect area;
};
复制代码

花朵展示视图FlowerContainerView

@interface FlowerContainerView : UIView

@property (nonatomic, strong) NSArray *flowerList;

@end

@implementation FlowerContainerView

- (void)drawRect:(CGRect)rect {
    for (NSValue *stateValue in self.flowerList) {
        struct ExtrinsicFlowerState state;
        [stateValue getValue:&state];

        UIView *flowerView = state.flowerView;
        CGRect frame = state.area;
        [flowerView drawRect:frame];
    }
}

@end
复制代码

控制器实现

static NSInteger kFlowerListCount = 200;

@interface ViewController ()

@property (nonatomic, strong) FlowerFactory *flowerFactory;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    FlowerContainerView *containerView = [[FlowerContainerView alloc] initWithFrame:self.view.bounds];
    containerView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:containerView];
    
    _flowerFactory = [[FlowerFactory alloc] init];
    NSMutableArray *flowerList = [[NSMutableArray alloc] initWithCapacity:kFlowerListCount];
    
    for (int i = 0; i < kFlowerListCount; ++i) {
        // 从工厂取得一个共享的花朵享元对象实例
        FlowerType type = arc4random() % kTotalNumberOfFlowTypes;
        UIView *flowerView = [_flowerFactory flowerViewWithType:type];
        
        // 设置花朵显示的区域
        CGRect viewBounds = self.view.bounds;
        CGFloat x = arc4random() % (int)CGRectGetWidth(viewBounds);
        CGFloat y = arc4random() % (int)CGRectGetHeight(viewBounds);
        CGFloat minSize = 10;
        CGFloat maxSize = 60;
        CGFloat size = (arc4random() % (int)(maxSize - minSize)) + minSize;
        
        struct ExtrinsicFlowerState state;
        state.flowerView = flowerView;
        state.area = CGRectMake(x, y, size, size);
        
        [flowerList addObject:[NSValue value:&state withObjCType:@encode(struct ExtrinsicFlowerState)]];
    }
    
    [containerView setFlowerList:flowerList.copy];
}


@end
复制代码
5.总结能够节省多少空间

通过享元对象能够节省的空间,取决于几个因素:

  • 通过共享减少的对象总数;
  • 每个对象中内在状态(即,可共享的状态)的数量;
  • 外在状态是计算出来的还是保存的;

然而,对共享对象外在状态的传递、查找和计算,可能产生运行时的开销,尤其在外在状态原本是作为内在状态来保存的时候.当享元的共享越来越多时,空间的节省会抵消这些开销.共享的享元越多,节省的存储就越多.节省直接跟共享的状态相关.如果对象有大量内在和外在状态,外在状态又能够计算出来而不用存储的时候,就能节省最大的空间.这样我们以两种方式节省了存储空间:共享减少了内在状态的开销,通过牺牲计算时间又节省了外在状态的存储空间.

代理

1.概述

为其他对象提供一种代理以控制对这个对象的访问.

2.使用场景
  • 需要一个远程代理,为位于不同地址空间或网络中的对象提供本地代表.
  • 需要一个虚拟代理,为根据要求创建重型的对象.例如懒加载图片.
  • 需要一个保护代理,来根据不同访问权限控制对原对象的访问.
  • 需要一个智能代理,通过对实体对象的引用进行计数来管理内存.也能用于锁定实体对象,让其他对象不能修改它.
3.结构UML图

4.代码实例 --让哆啦A梦帮忙送礼物

定义代理类SendGift

@protocol SendGift <NSObject>

- (void)sendGift;

@end
复制代码

哆啦A梦

@interface Doraemon : NSObject<SendGift>

@end
复制代码

雇主类

@interface Person : NSObject

@property (nonatomic, weak) id<SendGift> delegate;

@end
复制代码

调用场景

Person *daxiong = [[Person alloc] init];
Doraemon *doraemon = [[Doraemon alloc] init];
// 哆啦A梦成为代理,替大熊送礼物
daxiong.delegate = doraemon;
[daxiong.delegate sendGift];
复制代码
5.总结

主要思想是使用一个基本上跟实体对象行为相同的代理.客户端可以"透明地"使用代理,即不必知悉所面对的只是一个代理而不是实体对象.当客户端请求某些创建的开销较大的功能时,代理将把请求转发给实体对象,准备好请求的功能并返回给客户端.客户端不知道幕后发生了什么.

代理模式在OC中会常常会出现并且日常开发也会用到,最为典型的例子就是Applegate类了,它被委托处理应用生命周期期间所面对的各种状况.

对象状态

备忘录

1.概述

在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将对象恢复到原先保存的状态.

2.使用场景
  • 需要保存一个对象(或某部分)在某一个时刻的状态,这样以后就可以恢复到先前的状态.
  • 用于获取状态的接口会暴露实现的细节,需要将其隐藏起来.
3.结构UML图

4.代码实例--原发器创建一个包含其状态的备忘录,并传给看管人.看管人不知如何与备忘录交互,但会把备忘录放在安全之处保管好.

定义原发器类Originator

@interface Originator : NSObject

@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

- (Memento *)createMemento;
- (void)setMemento:(Memento *)memento;

@end

@implementation Originator

- (Memento *)createMemento {
    Memento *memo = [[Memento alloc] init];
    memo.age = self.age;
    memo.name = self.name;
    return memo;
}

- (void)setMemento:(Memento *)memento {
    self.age = memento.age;
    self.name = memento.name;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"name = %@, age = %ld", self.name, self.age];
}

@end

复制代码

定义备忘录类Memento

@interface Memento : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end
复制代码

定义看管者类Caretaker

@interface Caretaker : NSObject

- (void)archiveMemento:(Memento *)memento;
- (Memento *)getMemento;

@end

@interface Caretaker ()

@property (nonatomic, strong) Memento *memento;

@end

@implementation Caretaker

- (void)archiveMemento:(Memento *)memento {
    self.memento = memento;
}

- (Memento *)getMemento {
    return self.memento;
}

@end

复制代码

控制器实现

@implementation ViewController

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Originator *oriAtor = [[Originator alloc] init];
    oriAtor.name = @"sansa";
    oriAtor.age = 18;
    NSLog(@"1 --- %@", oriAtor);
    
    // 保存十八岁的样子
    Caretaker *taker = [[Caretaker alloc] init];
    [taker archiveMemento:[oriAtor createMemento]];
    
    // 过了好多年
    oriAtor.age = 78;
    NSLog(@"2 --- %@", oriAtor);
    
    // 吃了药剂,重返十八岁
    [oriAtor setMemento:[taker getMemento]];
    NSLog(@"3 --- %@", oriAtor);
}

@end

/* MementoPattern[38016:1796366] 1 --- name = sansa, age = 18
** MementoPattern[38016:1796366] 2 --- name = sansa, age = 78
** MementoPattern[38016:1796366] 3 --- name = sansa, age = 18
*/
复制代码
5.总结

**Cocoa Touch框架在归档、属性列表序列化和核心数据中采用了备忘录模式.

写在最后

百度百科

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。

不管是什么开发都会用到设计模式,学习好设计模式对我们对编码架构设计会有一个很好的提升.当然这也是会有相当大对学习成本,学习了理论后更需要去实践,Talk is cheap. Show me the code.理论和实践结合这样才会有更好的理解.

最后引用乔帮主的话Stay hungry Stay foolish共勉.

gitHub: PatternDemos

转载于:https://juejin.im/post/5cf3e8d56fb9a07ebe74a8fe

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值