24种设计模式代码实例学习(四)行为型模式

可恶的考试月终于结束了:((((

项目Demo

本文代码语言为Objective-C

上一篇文章中,我们介绍了结构型模式,在这一篇文章中,我们将来介绍行为型模式。

行为型模式和结构型模式、创建型模式一样,是设计模式中的主要分类。

前面我们介绍到,创建型模式更关注的是对象创建的灵活性和可维护性,结构型模式更关注的是系统结构的设计和组织。

而今天要介绍的行为型模式(Behavioral Patterns)关注的是对象之间的通信和交互方式,以实现特定的行为和责任分配

虽然行为型模式、结构型模式和创建型模式关注的方面不同,但它们之间也存在联系和互相影响。在实际应用中,这些模式往往会结合使用,以达到更好的设计和架构。例如,我们可以使用创建型模式创建对象,并使用结构型模式组织和管理这些对象,最后使用行为型模式定义对象之间的交互方式

0 常见的行为型模式

  1. 责任链模式(Chain of Responsibility Pattern)
  2. 观察者模式(Observer Pattern)
  3. 策略模式(Strategy Pattern)
  4. 模板方法模式(Template Method Pattern)
  5. 命令模式(Command Pattern)
  6. 解释器模式(Interpreter Pattern)
  7. 迭代器模式(Iterator Pattern)
  8. 中介者模式(Mediator Pattern)
  9. 备忘录模式(Memento Pattern)
  10. 状态模式(State Pattern)
  11. 访问者模式(Visitor Pattern)

在iOS 的实际开发中,责任链模式,观察者模式,策略模式,模板方法模式 这四种模式比较常用。

1 责任链模式(Chain of Responsibility Pattern)

image.png

责任链模式将请求沿着处理链传递,直到有一个处理者能够处理该请求。在责任链模式中,每个处理者都有一个对下一个处理者的引用,形成一个链条。

这种模式的主要目的是解耦发送者和接收者之间的关系发送者不需要知道具体是哪个接收者会处理请求,而接收者也不需要知道请求的发送者是谁。 通过这种方式,责任链模式可以灵活地添加、修改或者删除处理者,而不会对系统的其他部分产生影响。

责任链模式是在iOS 的实际开发中比较常用的模式:常用于处理事件或请求的传递。不同的对象可以按照一定的顺序处理事件,直到有对象能够处理该事件为止。

例子

简单例子

下面举一个简单的运用责任链模式的示例代码:

// 抽象处理者

@interface Handler : NSObject
@property (nonatomic, strong) Handler *nextHandler;
- (void)handleRequest:(NSInteger)request;
@end









@implementation Handler
- (void)handleRequest:(NSInteger)request {
    if (self.nextHandler) {
        [self.nextHandler handleRequest:request];
    }
}
@end










// 具体处理者A
@interface ConcreteHandlerA : Handler
@end





@implementation ConcreteHandlerA
- (void)handleRequest:(NSInteger)request {
    if (request < 10) {
        NSLog(@"ConcreteHandlerA 处理了请求 %ld", (long)request);
    } else if (self.nextHandler) {
        [self.nextHandler handleRequest:request];
    }
}

@end




// 具体处理者B
@interface ConcreteHandlerB : Handler
@end





@implementation ConcreteHandlerB
- (void)handleRequest:(NSInteger)request {
    if (request >= 10 && request < 20) {
        NSLog(@"ConcreteHandlerB 处理了请求 %ld", (long)request);
    } else if (self.nextHandler) {
        [self.nextHandler handleRequest:request];
    }
}

@end






// 具体处理者C
@interface ConcreteHandlerC : Handler
@end




@implementation ConcreteHandlerC
- (void)handleRequest:(NSInteger)request {
    if (request >= 20) {
        NSLog(@"ConcreteHandlerC 处理了请求 %ld", (long)request);
    } else if (self.nextHandler) {
        [self.nextHandler handleRequest:request];
    }
}
@end








客户端使用:

// 创建处理者对象

Handler *handlerA = [[ConcreteHandlerA alloc] init];
Handler *handlerB = [[ConcreteHandlerB alloc] init];
Handler *handlerC = [[ConcreteHandlerC alloc] init];
// 设置处理者之间的关系

handlerA.nextHandler = handlerB;
handlerB.nextHandler = handlerC;
// 发送请求
[handlerA handleRequest:5];
[handlerA handleRequest:15];
[handlerC handleRequest:25];

在这个示例中,抽象处理者(Handler)定义了一个处理请求的方法 handleRequest:,并持有一个对下一个处理者的引用 nextHandler。具体的处理者(ConcreteHandlerAConcreteHandlerBConcreteHandlerC)继承自抽象处理者,并实现了自己的处理逻辑。

客户端代码创建了具体处理者的实例,并通过设置 nextHandler 属性将它们链接在一起形成责任链。当一个请求被发送给第一个处理者时,它会首先尝试处理请求,如果不满足处理条件,则将请求传递给下一个处理者,直到有一个处理者能够处理请求或者到达链的末尾。

在上述代码中,客户端发送了三个请求:5、15和25。根据具体处理者的处理条件,请求被传递到了不同的处理者进行处理。输出结果如下:

ConcreteHandlerA 处理了请求 5
ConcreteHandlerB 处理了请求 15
ConcreteHandlerC 处理了请求 25

在iOS 开发中,责任链模式一般应用于以下场景:

  1. 事件处理:当一个事件需要经过多个对象进行处理时,可以使用责任链模式。例如,iOS中的事件传递机制就可以看作是责任链模式的应用。事件首先被传递给视图层次结构中的顶层视图,然后逐级向下传递,直到找到能够处理该事件的视图。
  2. 错误处理:当在应用中发生错误时,可以使用责任链模式来处理错误。每个处理者可以根据错误的类型、严重程度等条件来决定是否能够处理该错误。如果一个处理者无法处理该错误,将错误传递给下一个处理者,直到找到能够处理的处理者或者到达链的末尾。
  3. 请求过滤和验证:在网络请求或者数据处理中,可以使用责任链模式对请求进行过滤和验证。每个处理者可以根据请求的特定条件进行过滤和验证操作,例如验证请求的合法性、验证用户权限等。
  4. 消息传递和通知处理:当需要将消息或者通知传递给多个对象时,可以使用责任链模式。每个处理者可以根据消息的内容或者类型来决定是否处理该消息,并进行相应的操作。

请求过滤和验证例子

当涉及到网络请求或数据处理时,可以使用责任链模式对请求进行过滤和验证:

// 抽象处理者

@interface RequestHandler : NSObject
@property (nonatomic, strong) RequestHandler *nextHandler;
- (void)handleRequest:(NSDictionary *)request;
@end









@implementation RequestHandler
- (void)handleRequest:(NSDictionary *)request {
    // 默认情况下,将请求传递给下一个处理者
    if (self.nextHandler) {
        [self.nextHandler handleRequest:request];
    }
}
@end





// 具体处理者:验证请求合法性
@interface RequestValidityHandler : RequestHandler
@end






@implementation RequestValidityHandler
- (void)handleRequest:(NSDictionary *)request {
    if ([self isRequestValid:request]) {
        NSLog(@"请求合法");
        [super handleRequest:request];
    } else {
        NSLog(@"请求不合法,终止处理");
    }

}





- (BOOL)isRequestValid:(NSDictionary *)request {
    // 实现请求合法性的判断逻辑
    // ...
    return YES;
}
@end





// 具体处理者:验证用户权限
@interface UserPermissionHandler : RequestHandler
@end









@implementation UserPermissionHandler
- (void)handleRequest:(NSDictionary *)request {
    if ([self hasPermission:request]) {
        NSLog(@"用户权限验证通过");
        [super handleRequest:request];
    } else {
        NSLog(@"用户权限不足,终止处理");
    }
}




- (BOOL)hasPermission:(NSDictionary *)request {
    // 实现用户权限验证逻辑
    // ...
    return YES;
}

@end


// 具体处理者:处理请求
@interface RequestProcessingHandler : RequestHandler
@end


@implementation RequestProcessingHandler
- (void)handleRequest:(NSDictionary *)request {
    NSLog(@"处理请求中...");
    // 处理请求的逻辑
    // ...
    [super handleRequest:request];
}
@end




客户端代码:

// 创建处理者对象

RequestHandler *validHandler = [[RequestValidityHandler alloc] init];
RequestHandler *permissionHandler = [[UserPermissionHandler alloc] init];
RequestHandler *processingHandler = [[RequestProcessingHandler alloc] init];
// 设置处理者之间的关系

validHandler.nextHandler = permissionHandler;
permissionHandler.nextHandler = processingHandler;
// 创建请求
NSDictionary *request = @ {
    @"data" : @"要处理的数据",
    @"user" : @"当前用户",
    @"token" : @"访问token"
};
// 发送请求
[validHandler handleRequest:request];

在上述代码中,抽象处理者 RequestHandler 定义了处理请求的方法 handleRequest: 并持有对下一个处理者的引用 nextHandler。具体处理者 RequestValidityHandlerUserPermissionHandlerRequestProcessingHandler 分别实现了自己的请求过滤和验证、用户权限验证以及请求处理逻辑。

客户端代码创建了具体处理者的实例,并通过设置 nextHandler 属性将它们链接在一起形成责任链。当一个请求被发送时,它首先经过请求合法性验证处理者,然后是用户权限验证处理者,最后是请求处理处理者。每个处理者根据请求的特定条件进行过滤、验证或处理,并决定是否将请求传递给下一个处理者。

打印输出:

请求合法
用户权限验证通过
处理请求中...

责任链模式具有以下优点:

  1. 解耦:责任链模式将发送者和接收者解耦,发送者不需要知道具体的接收者,而接收者也不需要知道请求的发送者,使系统的各个部分之间的耦合度降低。
  2. 灵活性,可扩展性:责任链模式允许动态地添加、修改或删除处理者,可以根据实际需求灵活地调整处理链的结构和顺序,而不会影响其他部分的代码。
  3. 可维护性每个处理者只关注自己的职责,使得代码更加模块化和可维护

责任链模式也有一些缺点

  1. 请求的处理不保证被接收:由于每个处理者都有可能处理或者传递请求,因此不能保证请求一定会被处理或者接收者会被找到。如果责任链没有被正确地构建或者配置,请求可能会被忽略。
  2. 性能影响:在责任链中,请求需要经过多个处理者的依次处理,可能会导致处理链较长,影响性能。
  3. 可能导致系统变复杂:如果责任链的设计不当或者链条过长,可能会导致代码变得复杂,降低代码的可读性和可维护性。

综上所述,责任链模式在解耦、灵活性和可扩展性方面具有优点,但需要注意处理顺序、性能和代码复杂性等问题。

2 观察者模式(Observer Pattern)

image.png

观察者模式用于对象之间的一对多依赖关系。在该模式中,一个对象(称为主题或可观察者)维护一系列观察者对象,使得当主题状态发生变化时,所有观察者都会自动收到通知并进行相应的更新。

而在iOS开发中,通过使用KVO(Key-Value Observing)机制NotificationCenter 实现观察者模式,可以实现对象之间的松耦合通信

例子

在iOS 开发中,观察者模式经常使用NotificationCenter 来实现事件和数据的通知机制。NotificationCenter 是一个全局的通知中心,允许不同的对象发布和接收通知。

假设我们有一个需求,当用户完成登录操作后,需要通知其他模块进行相应的更新。我们可以使用观察者模式来实现这个功能。

首先,在登录完成后,我们会发送一个通知:

@implementation LoginViewController





- (void)viewDidLoad {

    [super viewDidLoad];

    self.view.backgroundColor = UIColor.blackColor;
    UIButton *loginBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [loginBtn setTitle:@"Login" forState:UIControlStateNormal];
    [loginBtn addTarget:self action:@selector(loginButtonTapped) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:loginBtn];
}










- (void)loginButtonTapped {
    // 执行登录操作
    // ...
    
    // 发送登录完成的通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"LoginCompletedNotification" object:nil];
}










@end


然后,在其他模块中,我们可以注册对该通知的观察,以便在收到通知时执行相应的操作:

@implementation ProfileViewController





- (void)viewDidLoad {

    [super viewDidLoad];

    self.view.backgroundColor = UIColor.grayColor;
    // 注册对登录完成通知的观察
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(loginCompletedNotificationReceived) name:@"LoginCompletedNotification"
                                               object:nil];
}










- (void)dealloc {
    // 在视图控制器销毁时,要取消观察
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}





- (void)loginCompletedNotificationReceived {
    // 收到登录完成通知后的操作
    // ...
    self.view.backgroundColor = UIColor.systemBlueColor;
    NSLog(@"登录成功");
}










@end



在客户端代码中,我们可以这样写:

ProfileViewController *proVC = [[ProfileViewController alloc] init];
LoginViewController *loginVC = [[LoginViewController alloc] init];
[self presentViewController:proVC animated:YES completion:nil];
[proVC presentViewController:loginVC animated:YES completion:nil];

这样,当点击登录按钮后,ProfileViewController背景颜色从原本的灰色变成蓝色,也打印输出”登录成功”。

ezgif.com-video-to-gif-2.gif

通过使用NotificationCenter,我们将发布者(发送通知的LoginViewController)与订阅者(收到通知的ProfileViewController)解耦,它们之间不直接进行通信。任何模块都可以注册对通知的观察,从而实现了解耦和通信。

这种方式在iOS 开发中广泛应用,比如在数据更新时发送通知,让相关的UI 模块进行刷新,或者在应用程序状态发生变化时发送通知,让其他模块作出相应的处理。观察者模式的使用可以大大简化不同模块之间的交互和通信,提高代码的可维护性和可扩展性。

3 策略模式(Strategy Pattern)

image.png

策略模式通过定义不同的策略对象,并将其封装在具体的类中,可以实现在运行时动态地选择不同的算法或行为。

策略模式将算法独立于使用它的客户端进行封装,使得算法可以独立于客户端的变化而变化。这种模式通过定义一系列可互换的算法族,并将每个算法封装起来,使它们可以相互替换。

例子

算法选择:当需要根据不同的条件或用户输入选择不同的算法时,可以使用策略模式。例如,对于一个计算器应用程序,可以根据用户选择的运算符使用不同的算法来执行计算:

首先,我们定义一个策略接口(Strategy)来声明所有具体策略类都需要实现的方法:

// 策略接口
@protocol Strategy <NSObject>
- (NSInteger)executeOperationWithNumber:(NSInteger)number1 andNumber:(NSInteger)number2;
@end











然后,我们实现几个具体的策略类,它们实现了策略接口的方法:

// 具体策略类:加法策略
@interface AdditionStrategy : NSObject <Strategy>
@end




@implementation AdditionStrategy
- (NSInteger)executeOperationWithNumber:(NSInteger)number1 andNumber:(NSInteger)number2 {
    return number1 + number2;
}
@end






// 具体策略类:减法策略
@interface SubtractionStrategy : NSObject <Strategy>
@end










@implementation SubtractionStrategy
- (NSInteger)executeOperationWithNumber:(NSInteger)number1 andNumber:(NSInteger)number2 {
    return number1 - number2;
}




@end


// 具体策略类:乘法策略
@interface MultiplicationStrategy : NSObject <Strategy>
@end





@implementation MultiplicationStrategy
- (NSInteger)executeOperationWithNumber:(NSInteger)number1 andNumber:(NSInteger)number2 {
    return number1 * number2;
}


@end





接下来,我们定义一个上下文类(Calculator),它包含一个指向策略接口的引用,并提供一个方法供客户端设置具体的策略:

// 上下文类:计算器
@interface Calculator : NSObject
@property (nonatomic, strong) id<Strategy> strategy;




- (NSInteger)performOperationWithNumber:(NSInteger)number1 andNumber:(NSInteger)number2;
@end





@implementation Calculator
- (NSInteger)performOperationWithNumber:(NSInteger)number1 andNumber:(NSInteger)number2 {
    return [self.strategy executeOperationWithNumber:number1 andNumber:number2];
}

@end






最后,在客户端代码中:

Calculator *calculator = [[Calculator alloc] init];





// 用户选择加法运算
calculator.strategy = [[AdditionStrategy alloc] init];
NSInteger result = [calculator performOperationWithNumber:5 andNumber:3];
NSLog(@"加法结果:%ld", result);




// 用户选择减法运算
calculator.strategy = [[SubtractionStrategy alloc] init];
result = [calculator performOperationWithNumber:5 andNumber:3];
NSLog(@"减法结果:%ld", result);







// 用户选择乘法运算
calculator.strategy = [[MultiplicationStrategy alloc] init];
result = [calculator performOperationWithNumber:5 andNumber:3];
NSLog(@"乘法结果:%ld", result);

运行上述代码,将会输出以下结果:

加法结果:8
减法结果:2
乘法结果:15

通过使用策略模式,我们可以根据用户选择的不同运算符,动态地切换不同的策略,从而实现不同的计算操作。这种模式使得算法的变化独立于客户端,并且提高了代码的灵活性和可维护性。

策略模式具有以下优点:

  1. 提供了更好的代码组织和结构:策略模式将每个具体策略类封装在单独的类中,使得代码结构清晰、可读性强。它将算法的实现与使用算法的客户端代码分离,降低了耦合性,使代码更易于维护和扩展。
  2. 提供了更高的灵活性和可扩展性:策略模式使得可以在运行时动态地切换不同的策略,而不需要修改客户端代码。新的策略类可以很容易地添加到系统中,从而增加了系统的灵活性和可扩展性。
  3. 使算法独立于客户端:策略模式将算法封装在策略类中,使得算法可以独立于客户端进行变化。客户端只需要知道如何选择和使用不同的策略,而无需了解具体策略的实现细节,提高了代码的抽象程度和可理解性。
  4. 促进了代码复用:通过定义通用的策略接口和多个具体策略类,可以促进代码的复用。不同的客户端可以共享相同的策略类,避免了代码的重复编写。

然而,策略模式也存在一些缺点:

  1. 增加了类的数量:引入策略模式会增加类的数量,特别是当策略较多时,可能会导致类的爆炸性增长,增加了代码的复杂性。
  2. 客户端需要了解不同的策略:虽然客户端代码与具体策略的实现分离,但客户端仍然需要了解不同策略的存在和选择。
  3. 策略的选择和切换开销:在运行时选择不同的策略会引入一定的开销。如果策略的选择频繁发生变化,可能会影响系统的性能。

4 模板方法模式(Template Method Pattern)

image.png

模板方法模式定义了一个操作中的算法的骨架将一些步骤延迟到子类中实现。模板方法允许子类在不改变算法结构的情况下重新定义算法的某些步骤。

在iOS开发中,通过定义一个抽象类或协议,并在其中定义一个模板方法,允许子类或遵循者实现特定的步骤,从而实现代码复用和统一的算法骨架

例子

// 基类
@interface AbstractClass : NSObject








- (void)templateMethod; // 模板方法






// 需要子类实现的抽象方法
- (void)primitiveOperation1;
- (void)primitiveOperation2;





@end










@implementation AbstractClass





- (void)templateMethod {
    // 执行算法的骨架
    [self primitiveOperation1];
    [self primitiveOperation2];
}










- (void)primitiveOperation1 {
    // 默认实现或者空实现,子类可以选择性地重写
}










- (void)primitiveOperation2 {
    // 默认实现或者空实现,子类可以选择性地重写
}




@end



// 子类 1
@interface ConcreteClass1 : AbstractClass





@end



@implementation ConcreteClass1





- (void)primitiveOperation1 {
    NSLog(@"ConcreteClass1: 执行步骤 1");
}












- (void)primitiveOperation2 {
    NSLog(@"ConcreteClass1: 执行步骤 2");
}






@end




// 子类 2
@interface ConcreteClass2 : AbstractClass




@end





@implementation ConcreteClass2


- (void)primitiveOperation1 {
    NSLog(@"ConcreteClass2: 执行步骤 1");
}


- (void)primitiveOperation2 {
    NSLog(@"ConcreteClass2: 执行步骤 2");
}


@end

客户端使用:

AbstractClass *class1 = [[ConcreteClass1 alloc] init];
[class1 templateMethod];
// Output:
// ConcreteClass1: 执行步骤 1
// ConcreteClass1: 执行步骤 2
AbstractClass *class2 = [[ConcreteClass2 alloc] init];
[class2 templateMethod];
// Output:
// ConcreteClass2: 执行步骤 1
// ConcreteClass2: 执行步骤 2

在上述示例中,AbstractClass是基类,定义了模板方法templateMethod和两个抽象方法primitiveOperation1primitiveOperation2。子类ConcreteClass1ConcreteClass2继承了AbstractClass,并实现了抽象方法。在使用示例中,我们可以看到不同子类的具体实现步骤在模板方法中被调用。这样,模板方法提供了算法的骨架,而具体实现由子类完成。

在iOS开发中,模板方法模式常常用于以下场景:

  1. UIViewController的生命周期方法:在iOS开发中,UIViewController 是一个常用的基类,用于管理视图控制器的生命周期和视图的显示。UIViewController 提供了一系列生命周期方法(例如viewDidLoadviewWillAppearviewWillDisappear等),开发者可以重写这些方法以添加自定义逻辑。这里的生命周期方法可以看作是模板方法,定义了视图控制器的整体行为骨架,而具体的实现可以由子类重写。
  2. UITableView和UICollectionView的数据源方法UITableViewUICollectionView 是iOS 开发中常用的用于显示列表和网格的视图控件。它们使用数据源协议(UITableViewDataSourceUICollectionViewDataSource)来提供数据和配置单元格。这些数据源方法(例如numberOfSectionsInTableViewnumberOfRowsInSectioncellForRowAtIndexPath等)可以看作是模板方法,定义了列表或网格的整体结构,而具体的实现由数据源对象提供。

在这些场景中,模板方法模式能够提供一个框架或者约定,使得基类或协议定义了整体行为的骨架,而具体的实现可以由子类或对象来提供。这种模式可以提高代码的可重用性和可维护性,同时也能够提供一致的编程接口和约束,方便开发者进行扩展和定制。

模板方法模式也有一些缺点:

  1. 限制了部分自由度:模板方法模式在定义算法骨架时,将一些步骤固定下来,子类只能重写或扩展指定的方法。
  2. 增加了类的个数:使用模板方法模式需要定义基类和多个子类,这样会增加类的个数和代码量。如果算法的变化较小或者只有少数几个子类需要不同的实现,使用模板方法模式可能会显得过于繁琐
  3. 难以控制子类的行为:在模板方法模式中,子类可以通过重写方法来实现特定步骤的定制,这也意味着子类可能会对算法的整体行为产生影响

5 命令模式(Command Pattern)

image.png

命令模式用于将请求(命令)封装成一个对象,从而允许根据不同的请求参数来参数化客户端对象。通过使用命令模式,可以将方法调用、请求或操作封装到单个对象中,使得我们可以将这些命令队列、记录日志、撤销操作或进行重做等。

在命令模式中,有四个核心组件

  1. 命令(Command):命令对象封装了对特定操作的请求。它通常包含执行操作的方法。
  2. 发送者(Invoker):发送者是一个对象,它知道如何触发命令来执行特定的操作。它将命令对象与接收者对象解耦
  3. 接收者(Receiver):接收者是实际执行操作的对象。命令模式通过将命令与接收者分离,使得可以独立地改变接收者或命令,而不需要修改发送者。
  4. 客户端(Client):客户端创建具体的命令对象并设置其接收者。

例子

简单例子

首先,我们创建一个命令接口 Command,其中包含一个执行方法 execute

// Command.h
@protocol Command <NSObject>
- (void)execute;
@end





接下来,我们实现接收者类 Receiver,其中包含执行实际操作的方法:

// Receiver.h
@interface Receiver : NSObject
- (void)performAction;
@end











// Receiver.m
@implementation Receiver




- (void)performAction {
    NSLog(@"Receiver: Performing action.");
}








@end










然后,我们实现具体的命令类 ConcreteCommand,它遵循命令接口,并将操作请求委托给接收者对象:

// ConcreteCommand.h
#import "Command.h"

#import "Receiver.h"




@interface ConcreteCommand : NSObject <Command>
@property (nonatomic, strong) Receiver *receiver;
- (instancetype)initWithReceiver:(Receiver *)receiver;
@end






// ConcreteCommand.m
#import "ConcreteCommand.h"







@implementation ConcreteCommand







- (instancetype)initWithReceiver:(Receiver *)receiver {
    self = [super init];

    if (self) {

        _receiver = receiver;
    }


    return self;


}






- (void)execute {

    [self.receiver performAction];
}




@end




最后,我们实现发送者类 Invoker,它接受命令对象并调用其执行方法:

// Invoker.h
#import "Command.h"









@interface Invoker : NSObject
- (void)setCommand:(id<Command>)command;
- (void)executeCommand;
@end






// Invoker.m
@interface Invoker ()
@property (nonatomic, strong) id<Command> command;
@end






@implementation Invoker




- (void)setCommand:(id<Command>)command {
    self.command = command;
}










- (void)executeCommand {
    [self.command execute];
}










@end



现在,我们可以在客户端代码中使用这些类:

// 创建接收者对象
Receiver *receiver = [[Receiver alloc] init];
// 创建具体命令对象并将接收者传递给它
ConcreteCommand *command = [[ConcreteCommand alloc] initWithReceiver:receiver];
// 创建发送者对象,并设置命令
Invoker *invoker = [[Invoker alloc] init];
[invoker setCommand:command];
// 发送者触发命令执行
[invoker executeCommand];

这个例子中,命令模式将一个特定的操作(performAction)封装在一个命令对象中。发送者(Invoker)通过持有命令对象,可以触发命令的执行,而无需直接与接收者(Receiver)进行交互。这种解耦允许我们灵活地替换命令或接收者,以满足不同的需求。

在iOS开发中,命令模式可以在以下几个常见场景中使用:

  1. 撤销和重做操作:命令模式可以用于实现撤销和重做操作。通过将操作封装成命令对象,可以将每个操作保存在一个历史记录中,并在需要时按照顺序执行或撤销。这在图形编辑器、文本编辑器等应用程序中非常有用。
  2. 动作菜单和工具栏:命令模式可以用于实现动作菜单和工具栏。每个菜单项或工具栏按钮可以关联一个命令对象,并在触发时执行相应的命令。这种方式使得用户界面的动作和操作与实际的命令对象解耦,使得用户界面更加灵活和可扩展。
  3. 异步任务队列:命令模式可以用于管理异步任务队列。每个命令对象可以代表一个需要执行的异步任务,将任务的执行封装在命令对象中。通过使用命令模式,可以实现任务队列的管理、优先级控制、任务取消等功能。

异步任务队列

命令模式管理异步任务队列,实现添加、删除和执行任务,可以实现任务的优先级控制,也可以支持任务的暂停。

异步任务命令类:

@interface AsyncTaskCommand : NSObject <Command>





@property (nonatomic, strong) NSString *taskName;
@property (nonatomic) NSInteger priority;
@property (nonatomic) BOOL paused;






- (instancetype)initWithTaskName:(NSString *)taskName priority:(NSInteger)priority;




@end






@implementation AsyncTaskCommand







- (instancetype)initWithTaskName:(NSString *)taskName priority:(NSInteger)priority {
    self = [super init];
    if (self) {
        _taskName = taskName;
        _priority = priority;
        _paused = NO;
    }


    return self;


}






- (void)execute {

    if (self.paused) {
        NSLog(@"任务已暂停:%@", self.taskName);
        return;
    }

    // 执行具体的异步任务,例如发送网络请求、读取文件等
    NSLog(@"执行异步任务:%@", self.taskName);
}







- (void)pause {
    self.paused = YES;
}







- (void)resume {
    self.paused = NO;
}












- (NSInteger)priority {
    return _priority;
}






@end


异步任务队列类:

@interface AsyncTaskQueue : NSObject





@property (nonatomic, strong) NSMutableArray<id <Command>> *taskQueue;




- (void)addTask:(id<Command>)task;
- (void)removeTask:(id<Command>)task;
- (void)executeTasks;
- (void)pauseTasks;
- (void)resumeTasks;




@end







@implementation AsyncTaskQueue







- (instancetype)init {
    self = [super init];

    if (self) {

        _taskQueue = [NSMutableArray array];
    }


    return self;


}






- (void)addTask:(id<Command>)task {
    [self.taskQueue addObject:task];
}




- (void)removeTask:(id<Command>)task {
    [self.taskQueue removeObject:task];
}




- (void)executeTasks {
    NSLog(@"executeTasks");
    NSArray<id<Command>> *sortedTasks = [self.taskQueue sortedArrayUsingComparator:^NSComparisonResult(id<Command> task1, id<Command> task2) {
            NSInteger priority1 = [task1 priority];
            NSInteger priority2 = [task2 priority];
            if (priority1 < priority2) {
                return NSOrderedAscending;  // 排在前面
            } else if (priority1 > priority2) {
                return NSOrderedDescending;
            } else {
                return NSOrderedSame;
            }
        }];
    
    for (id<Command> task in sortedTasks) {
        [task execute];
    }
    [self.taskQueue removeAllObjects];
}




- (void)pauseTasks {
    NSLog(@"pauseTasks");
    for (id<Command> task in self.taskQueue) {
        if ([task respondsToSelector:@selector(pause)]) {
            [task pause];
        }
    }
}


- (void)resumeTasks {
    NSLog(@"resumeTasks");
    for (id<Command> task in self.taskQueue) {
        if ([task respondsToSelector:@selector(resume)]) {
            [task resume];
        }
    }
}

@end

在客户端代码中可以这样写:

// 创建异步任务队列
AsyncTaskQueue *taskQueue = [[AsyncTaskQueue alloc] init];
// 创建异步任务命令对象并添加到队列
AsyncTaskCommand *task1 = [[AsyncTaskCommand alloc] initWithTaskName:@"Task 1" priority:1];
AsyncTaskCommand *task2 = [[AsyncTaskCommand alloc] initWithTaskName:@"Task 2" priority:3];
AsyncTaskCommand *task3 = [[AsyncTaskCommand alloc] initWithTaskName:@"Task 3" priority:2];
[taskQueue addTask:task1];
[taskQueue addTask:task2];
[taskQueue addTask:task3];
// 执行异步任务队列中的任务
[taskQueue executeTasks];
// 暂停任务
[taskQueue pauseTasks];
// 继续执行任务
[taskQueue resumeTasks];
// 移除任务
[taskQueue removeTask:task2];
// 执行剩下的任务
[taskQueue executeTasks];

在上述代码中,我们创建了一个 AsyncTaskQueue 类来管理异步任务队列,包括添加任务、删除任务、执行任务等功能。通过创建 AsyncTaskCommand 类来表示具体的异步任务,并添加了任务的优先级和暂停状态的支持。通过调用 executeTasks 方法,可以执行任务队列中的所有任务,并根据任务的优先级进行排序和执行。还添加了 pauseTasksresumeTasks 方法来暂停和恢复任务的执行。在示例代码中,我们演示了添加、删除、执行任务,以及任务的暂停和恢复的功能。

通过使用命令模式管理异步任务队列,我们可以方便地添加、删除和执行任务,可以根据任务的优先级控制任务的执行顺序,并且可以支持任务的暂停和恢复操作。

6 解释器模式(Interpreter Pattern)

image.png

解释器模式(Interpreter Pattern)用于定义一种语言的文法规则,并且解释和执行该语言中的表达式。该模式将一个问题领域划分为一组类,每个类代表语言中的一个文法规则,而解释器则使用这些类来解释语言中的表达式。

解释器模式主要由以下几个角色组成:

  • 抽象表达式(Abstract Expression):定义了一个抽象的解释方法 interpret,所有具体表达式都要实现该方法。
  • 终结符表达式(Terminal Expression):实现了抽象表达式的解释方法,它代表语言中的终结符,不能再进行进一步解释。
  • 非终结符表达式(Nonterminal Expression):实现了抽象表达式的解释方法,它代表语言中的非终结符,可以继续进行进一步解释。
  • 上下文(Context):包含解释器解释的全局信息。
  • 客户端(Client):创建并配置表达式的解释器,并调用解释方法解析语言中的表达式。

(事实上,解释器模式在iOS 开发中比较少用)

例子

假设我们有一个简单的算术表达式语言,可以计算加法和减法:

// 抽象表达式
@protocol Expression <NSObject>
- (NSInteger)interpretWithContext:(NSDictionary<NSString *, NSNumber *> *)context;
@end











// 终结符表达式
@interface NumberExpression : NSObject <Expression>
@property (nonatomic, assign) NSInteger number;
@end






@implementation NumberExpression







- (NSInteger)interpretWithContext:(NSDictionary<NSString *, NSNumber *> *)context {
    return self.number;
}





@end





// 非终结符表达式 - 加法
@interface AdditionExpression : NSObject <Expression>
@property (nonatomic, strong) id<Expression> leftExpression;
@property (nonatomic, strong) id<Expression> rightExpression;
@end





@implementation AdditionExpression



- (NSInteger)interpretWithContext:(NSDictionary<NSString *, NSNumber *> *)context {
    NSInteger leftValue = [self.leftExpression interpretWithContext:context];
    NSInteger rightValue = [self.rightExpression interpretWithContext:context];
    return leftValue + rightValue;
}





@end



// 非终结符表达式 - 减法
@interface SubtractionExpression : NSObject <Expression>
@property (nonatomic, strong) id<Expression> leftExpression;
@property (nonatomic, strong) id<Expression> rightExpression;
@end









@implementation SubtractionExpression




- (NSInteger)interpretWithContext:(NSDictionary<NSString *, NSNumber *> *)context {
    NSInteger leftValue = [self.leftExpression interpretWithContext:context];
    NSInteger rightValue = [self.rightExpression interpretWithContext:context];
    return leftValue - rightValue;
}


@end



// 上下文
@interface Context : NSObject
@property (nonatomic, strong) NSDictionary<NSString *, NSNumber *> *variables;



- (NSNumber *)valueForVariable:(NSString *)variableName;






@end



@implementation Context


- (NSNumber *)valueForVariable:(NSString *)variableName {
    return self.variables[variableName];
}

@end


在客户端代码中:

Context *context = [[Context alloc] init];
context.variables = @{
    @"x" : @(10),
    @"y" : @(5)
};
AdditionExpression *expression = [[AdditionExpression alloc] init];
expression.leftExpression = [[NumberExpression alloc] init];
((NumberExpression *)expression.leftExpression).number = [context valueForVariable:@"x"].integerValue;
expression.rightExpression = [[SubtractionExpression alloc] init];
((SubtractionExpression *)expression.rightExpression).leftExpression = [[NumberExpression alloc] init];
((NumberExpression *)((SubtractionExpression *)expression.rightExpression).leftExpression).number = [context
valueForVariable:@"y"].integerValue;
((SubtractionExpression *)expression.rightExpression).rightExpression = [[NumberExpression alloc] init];
((NumberExpression *)((SubtractionExpression *)expression.rightExpression).rightExpression).number = 2;
NSInteger result = [expression interpretWithContext:context.variables];
NSLog(@"Result: %ld", (long)result);

在上面的示例中,我们定义了抽象表达式 Expression,并实现了终结符表达式 NumberExpression 和非终结符表达式 AdditionExpressionSubtractionExpression。上下文 Context 包含了表达式解释所需的全局信息。在客户端代码中,我们创建了一个算术表达式,并使用上下文中的变量进行解释和计算,最后输出计算结果。

但是也可以看到,类型检查和类型转换非常繁琐,如果使用Swift 这样的语言,可能会得到改善,但事实上,在iOS 实际开发中,解释器模式确实比较少用到,它通常在需要构建一种特定语言的解释器或者需要解析复杂的表达式时使用。

7 迭代器模式(Iterator Pattern)

image.png

迭代器模式提供了一种访问集合对象元素的方法,而无需暴露集合的内部表示。通过使用迭代器模式,可以在不暴露集合内部结构的情况下,按顺序访问集合中的元素。

通常,可以使用迭代器模式来遍历集合对象(如数组、字典等)

例子

// 迭代器接口
@protocol Iterator <NSObject>








- (BOOL)hasNext;
- (id)next;






@end






// 集合接口
@protocol Aggregate <NSObject>








- (id<Iterator>)createIterator;





@end





// 具体迭代器类
@interface ConcreteIterator : NSObject <Iterator>




@property (nonatomic, strong) NSArray *collection;
@property (nonatomic, assign) NSInteger currentIndex;



- (instancetype)initWithCollection:(NSArray *)collection;







@end




@implementation ConcreteIterator



- (instancetype)initWithCollection:(NSArray *)collection {
    self = [super init];
    if (self) {
        _collection = collection;
        _currentIndex = 0;
    }
    return self;
}







- (BOOL)hasNext {
    return self.currentIndex < self.collection.count;
}












- (id)next {
    if (![self hasNext]) {
        return nil;
    }
    id item = self.collection[self.currentIndex];
    self.currentIndex++;
    return item;
}





@end





// 具体集合类
@interface ConcreteAggregate : NSObject <Aggregate>



@property (nonatomic, strong) NSArray *collection;






- (instancetype)initWithCollection:(NSArray *)collection;



@end


@implementation ConcreteAggregate


- (instancetype)initWithCollection:(NSArray *)collection {
    self = [super init];
    if (self) {
        _collection = collection;
    }
    return self;
}




- (id<Iterator>)createIterator {
    return [[ConcreteIterator alloc] initWithCollection:self.collection];
}

@end

客户端代码可以这样写:

NSArray *array = @[@"Item 1", @"Item 2", @"Item 3", @"Item 4"];
// 创建具体集合对象
ConcreteAggregate *aggregate = [[ConcreteAggregate alloc] initWithCollection:array];
// 创建迭代器对象
id<Iterator> iterator = [aggregate createIterator];
// 使用迭代器遍历集合对象
while ([iterator hasNext]) {
    id item = [iterator next];
    NSLog(@"%@", item);
}


在上面的代码中,我们首先定义了迭代器接口(Iterator)和集合接口(Aggregate)。然后,我们实现了具体的迭代器类(ConcreteIterator),它维护了一个集合对象和当前元素的索引,实现了迭代器接口中的方法。接着,我们实现了具体的集合类(ConcreteAggregate),它实现了集合接口,并在createIterator方法中返回一个具体迭代器对象。最后,我们使用迭代器来遍历集合对象的元素。

使用迭代器模式的好处是,客户端代码可以通过统一的迭代器接口来访问不同类型的集合对象,而无需关心集合内部的实现细节。这样可以使代码更加灵活、可扩展,并且符合面向对象的设计原则。

8 中介者模式(Mediator Pattern)

image.png

中介者模式通过封装一系列对象之间的交互,将对象之间的通信转变为通过中介者进行的集中式通信。中介者模式的目标是减少对象之间的直接耦合,通过引入一个中介者对象,使得对象之间的交互更加灵活、可维护和可扩展

在中介者模式中,各个对象不再直接相互通信,而是通过中介者进行通信。当一个对象发生改变时,它不需要知道具体需要通知哪些对象,而是将消息发送给中介者,由中介者来处理通知其他相关对象。这样,对象之间的耦合度降低,它们只需要和中介者进行通信,而不需要了解其他对象的具体细节。

例子

现在有两个同事需要通过中介者通知:

// 中介者接口
@protocol Mediator <NSObject>
- (void)sendMessage:(NSString *)message fromColleague:(id)colleague;
@end











// 具体中介者
@interface ConcreteMediator : NSObject <Mediator>
@property (nonatomic, strong) id colleague1;
@property (nonatomic, strong) id colleague2;
@end










@implementation ConcreteMediator
- (void)sendMessage:(NSString *)message fromColleague:(id)colleague {
    if (colleague == self.colleague1) {
        // colleague1发送消息时,通知colleague2
        [self.colleague2 receiveMessage:message];
    } else if (colleague == self.colleague2) {
        // colleague2发送消息时,通知colleague1
        [self.colleague1 receiveMessage:message];
    }
}



@end







// 抽象同事类
@interface Colleague : NSObject
@property (nonatomic, weak) id<Mediator> mediator;
- (instancetype)initWithMediator:(id<Mediator>)mediator;
- (void)send:(NSString *)message;
- (void)receiveMessage:(NSString *)message;
@end


@implementation Colleague
- (instancetype)initWithMediator:(id<Mediator>)mediator {
    self = [super init];
    if (self) {
        _mediator = mediator;
    }
    return self;
}












- (void)send:(NSString *)message {
    [self.mediator sendMessage:message fromColleague:self];
}






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

@end




// 具体同事类
@interface ConcreteColleague1 : Colleague
@end


@implementation ConcreteColleague1
@end








@interface ConcreteColleague2 : Colleague
@end


@implementation ConcreteColleague2
@end

客户端代码可以这样写:

// 中介者
ConcreteMediator *mediator = [[ConcreteMediator alloc] init];
// 两个通信的同事
Colleague1 *colleague1 = [[Colleague1 alloc] initWithMediator:mediator];
Colleague2 *colleague2 = [[Colleague2 alloc] initWithMediator:mediator];
mediator.colleague1 = colleague1;
mediator.colleague2 = colleague2;
// 通信
[colleague1 send:@"Hello, colleague2!"];
[colleague2 send:@"Hello, colleague1!"];

在上述示例中,Mediator是中介者接口,定义了sendMessage:fromColleague:方法。ConcreteMediator是具体的中介者类,实现了中介者接口,并在sendMessage:fromColleague:方法中根据不同的发送者通知其他同事对象。

Colleague是抽象同事类,其中包含了中介者对象,并定义了send:receiveMessage:方法。ConcreteColleague1ConcreteColleague2是具体的同事类,继承自抽象同事类,分别实现了具体的发送和接收行为。

在使用示例中,创建了一个具体的中介者对象mediator,以及两个具体的同事对象colleague1colleague2。通过设置中介者的同事对象,并通过调用同事对象的send:方法发送消息,触发中介者对象的消息传递过程。

colleague1发送消息时,中介者对象mediator将消息传递给colleague2colleague2收到消息后打印出来。同样地,当colleague2发送消息时,中介者对象mediator将消息传递给colleague1colleague1收到消息后打印出来。

这样,通过中介者模式,对象之间的通信通过中介者进行集中处理,实现了对象之间的解耦。

在iOS开发中,中介者模式常常应用于以下场景:

  1. 视图控制器之间的通信:在iOS应用程序中,ViewController之间需要进行数据传递和交互。使用中介者模式可以将这些通信逻辑抽象到一个中介者对象中,视图控制器只需要与中介者进行通信,而无需直接依赖其他视图控制器。这样可以降低视图控制器之间的耦合度,使代码更加清晰和可维护。
  2. 多个模块之间的通信:在大型iOS应用程序中,通常由多个模块或组件组成,它们之间需要进行数据传递和交互。使用中介者模式可以引入一个中介者对象,用于集中处理模块之间的通信。 模块只需要与中介者进行通信,而无需了解其他模块的具体细节,从而实现模块之间的解耦和灵活性。

从上面的应用也可以看到,中介者模式有减少对象之间耦合性增加复用性和扩展性集中控制交互逻辑的优点。同时,中介者模式也可能存在中介对象随着通信的对象变多而变的复杂,臃肿,难以维护,还有增加系统的复杂性的缺点。

9 备忘录模式(Memento Pattern)

image.png

备忘录模式(Memento Pattern)用于在不破坏封装性的前提下捕获和恢复对象的内部状态。该模式通过创建一个备忘录对象来存储对象的状态,并将其保存在原始对象之外,以便在需要时恢复状态。

在备忘录模式中,通常涉及三个主要角色:

  1. 发起人(Originator):它是需要保存状态的对象。它可以创建一个备忘录来保存当前状态,也可以使用备忘录来恢复之前保存的状态
  2. 备忘录(Memento):它是保存发起人对象状态的对象。它通常具有能够获取发起人状态的方法,以及设置发起人状态的方法。
  3. 管理者(Caretaker):它负责保存和恢复备忘录对象。它通常会保存多个备忘录对象,并可以选择在适当的时候将其提供给发起人。

例子

// 备忘录对象
@interface Memento : NSObject








@property (nonatomic, strong) NSString *state;






@end





@implementation Memento





@end










// 发起人对象
@interface Originator : NSObject







@property (nonatomic, strong) NSString *state;




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






@end





@implementation Originator







- (Memento *)createMemento {
    Memento *memento = [[Memento alloc] init];
    memento.state = self.state;
    return memento;
}





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


@end



// 管理者对象
@interface Caretaker : NSObject

@property (nonatomic, strong) NSMutableArray<Memento *> *mementos;








@end






@implementation Caretaker




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

- (void)addMemento:(Memento *)memento {
    [self.mementos addObject:memento];
}







- (Memento *)getMementoAtIndex:(NSUInteger)index {
    if (index < self.mementos.count) {
        return self.mementos[index];
    }
    return nil;
}

@end

客户端代码可以这样写:

// 使用备忘录模式
Originator *originator = [[Originator alloc] init];
Caretaker *caretaker = [[Caretaker alloc] init];
// 设置发起人的状态
originator.state = @"state1";
// 创建备忘录并保存状态
Memento *memento1 = [originator createMemento];
[caretaker addMemento:memento1];
// 修改发起人的状态
originator.state = @"state2";
// 创建备忘录并保存状态
Memento *memento2 = [originator createMemento];
[caretaker addMemento:memento2];
// 恢复到第一个备忘录保存的状态
Memento *saveMemento = [caretaker getMementoAtIndex:0];
[originator restoreFromMemento:saveMemento];
NSLog(@"Current state: %@", originator.state);

在上述示例中,Originator代表发起人对象,它具有状态属性state。它通过createMemento方法创建一个备忘录对象并保存当前状态,通过restoreFromMemento方法从备忘录对象中恢复状态。

Memento表示备忘录对象,它具有一个state属性用于保存发起人的状态。

Caretaker充当管理者对象,它负责保存和恢复备忘录对象。它使用一个可变数组mementos来保存多个备忘录对象,并提供了addMementogetMementoAtIndex方法来添加和获取备忘录。

在使用备忘录模式时,发起人可以创建备忘录对象并将其交给管理者保存。如果需要恢复之前的状态,可以从管理者那里获取相应的备忘录对象,并通过发起人对象恢复状态。这样可以在不破坏封装性的情况下实现状态的保存和恢复。

备忘录模式在iOS开发中常用于需要保存和恢复对象状态的场景,尤其是在撤销和恢复功能、数据持久化、界面状态管理以及游戏状态管理等方面。通过使用备忘录模式,可以更好地控制对象的状态,并提供灵活的状态管理机制。

可以看出,备忘录模式具有状态保存和恢复的分离,支持撤销和恢复的优点,但同时,备忘录模式也存在内存消耗过大会暴露对象原始属性的缺点。

10 状态模式(State Pattern)

image.png

状态模式(State Pattern)允许对象在内部状态改变时改变其行为,使其看起来像是修改了自身的类。状态模式通过将状态封装成独立的类,使得状态的变化不直接影响到对象的行为,从而实现了状态与行为的解耦。

在状态模式中,对象根据内部状态的不同而改变其行为。它包含以下主要角色:

  1. Context(上下文):定义客户端感兴趣的接口,并维护一个具体状态类的实例,这个实例定义当前状态。
  2. State(状态):定义一个接口,用于封装与Context的特定状态相关的行为。
  3. ConcreteState(具体状态):每个具体状态类实现State接口,并实现与该状态相关的行为。

例子

假设我们正在构建一个音乐播放器应用,其中包含多个播放状态(暂停、播放、停止):

// 状态接口
@protocol State <NSObject>








- (void)play;
- (void)pause;
- (void)stop;




@end






// 具体状态类:播放状态
@interface PlayState : NSObject <State>







@end










@implementation PlayState




- (void)play {
    NSLog(@"当前已经在播放音乐");
}


- (void)pause {
    NSLog(@"暂停音乐");
}




- (void)stop {
    NSLog(@"停止播放音乐");
}



@end





// 具体状态类:暂停状态
@interface PauseState : NSObject <State>


@end



@implementation PauseState

- (void)play {
    NSLog(@"继续播放音乐");
}


- (void)pause {
    NSLog(@"音乐已经暂停");
}

- (void)stop {
    NSLog(@"停止播放音乐");
}





@end





// 具体状态类:停止状态
@interface StopState : NSObject <State>



@end








@implementation StopState



- (void)play {
    NSLog(@"开始播放音乐");
}


- (void)pause {
    NSLog(@"音乐已经停止");
}


- (void)stop {
    NSLog(@"音乐已经停止");
}




@end

// 上下文类
@interface MusicPlayer : NSObject

@property (nonatomic, strong) id<State> currentState;

- (void)playMusic;
- (void)pauseMusic;
- (void)stopMusic;

@end

@implementation MusicPlayer

- (instancetype)init {
    self = [super init];
    if (self) {
        // 初始状态为停止状态
        _currentState = [[StopState alloc] init];
    }
    return self;
}

- (void)playMusic {
    [self.currentState play];
    self.currentState = [[PlayState alloc] init];
}

- (void)pauseMusic {
    [self.currentState pause];
    self.currentState = [[PauseState alloc] init];
}

- (void)stopMusic {
    [self.currentState stop];
    self.currentState = [[StopState alloc] init];
}

@end

而在客户端上,我们对于不同状态的变换,只需要简单使用MusicPlayer

MusicPlayer *player = [[MusicPlayer alloc] init];
[player playMusic];  // output:开始播放音乐
[player pauseMusic];  // output:暂停音乐
[player playMusic];  // output:继续播放音乐
[player stopMusic];  // output:停止播放音乐

在上面的示例中,我们定义了一个音乐播放器应用的上下文类MusicPlayer和三个具体状态类PlayStatePauseStateStopState,它们实现了状态接口State。在MusicPlayer中,我们通过维护一个currentState实例变量来跟踪当前状态,并且在状态改变时更新它。客户端代码通过调用playMusicpauseMusicstopMusic等方法来操作音乐播放器,具体的行为由当前状态对象处理。

这样,当我们需要添加新的播放状态时,只需创建一个新的具体状态类并实现相应的行为方法,而不需要修改MusicPlayer类的代码,实现了状态和行为的解耦。

当然,上面这个例子只是帮助理解状态模式,在iOS 实际开发中,状态模式适用于对象有多个状态且状态之间有复杂的转换逻辑的情况。(比如:视图控制器生命周期管理:视图控制器在其生命周期中经历多个状态,如viewDidLoadviewWillAppearviewDidAppearviewWillDisappear等。可以使用状态模式来管理这些状态,并根据不同的状态执行相应的操作,以实现良好的生命周期管理)它可以使代码更加模块化、灵活和可扩展。但在简单的场景中,引入状态模式可能会过于复杂,不切实际

11 访问者模式(Visitor Pattern)

image.png

访问者模式(Visitor Pattern允许我们在不修改对象结构的情况下定义对对象的新操作。该模式适用于需要对一个复杂对象结构中的各个元素进行不同操作的情况。

访问者模式基于两个核心概念:元素(Element访问者(Visitor)。元素是一个具体对象结构,它定义了接受访问者对象的方法。访问者是一个表示新操作的对象,它定义了访问元素的方法。通过将访问者对象传递给元素,元素可以将自身委托给访问者来执行特定的操作

例子

首先,我们定义元素接口 Element,包含一个接受访问者对象的方法 acceptVisitor:

// Element.h
@protocol Element <NSObject>
- (void)acceptVisitor:(id)visitor;
@end





接下来,我们定义访问者接口 Visitor,其中包含了针对不同元素的访问方法:

@class ConcreteElementA;
@class ConcreteElementB;








@protocol Visitor <NSObject>
- (void)visitElementA:(ConcreteElementA *)elementA;
- (void)visitElementB:(ConcreteElementB *)elementB;
@end


然后,我们定义两个具体元素类 ConcreteElementAConcreteElementB,它们实现了 Element 接口,并根据需要调用访问者的相应方法:

// ConcreteElementA.h
#import "Element.h"








@interface ConcreteElementA : NSObject <Element>
@end









// ConcreteElementA.m
#import "ConcreteElementA.h"





@implementation ConcreteElementA








- (void)acceptVisitor:(id)visitor {
    [visitor visitElementA:self];
}




- (NSString *)operationA {
    return @"ConcreteElementA operation";
}










@end





// ConcreteElementB.h
#import "Element.h"




@interface ConcreteElementB : NSObject <Element>
@end



// ConcreteElementB.m
#import "ConcreteElementB.h"




@implementation ConcreteElementB





- (void)acceptVisitor:(id)visitor {
    [visitor visitElementB:self];
}







- (NSString *)operationB {
    return @"ConcreteElementB operation";
}












@end






然后,我们实现具体的访问者类 ConcreteVisitor,其中实现了对两个具体元素的不同操作:

// ConcreteVisitor.h
#import "Visitor.h"








@interface ConcreteVisitor : NSObject <Visitor>
@end









// ConcreteVisitor.m
#import "ConcreteVisitor.h"
#import "ConcreteElementA.h"
#import "ConcreteElementB.h"








@implementation ConcreteVisitor





- (void)visitElementA:(ConcreteElementA *)elementA {
    NSString *result = [elementA operationA];
    NSLog(@"Visitor is operating on ElementA: %@", result);
}




- (void)visitElementB:(ConcreteElementB *)elementB {
    NSString *result = [elementB operationB];
    NSLog(@"Visitor is operating on ElementB: %@", result);
}










@end



最后,在客户端代码中,我们可以这样使用访问者模式:

ConcreteElementA *elementA = [[ConcreteElementA alloc] init];
ConcreteElementB *elementB = [[ConcreteElementB alloc] init];
ConcreteVisitor *visitor = [[ConcreteVisitor alloc] init];
// 通过将访问者对象传递给元素,元素可以将自身委托给访问者来执行特定的操作
[elementA acceptVisitor:visitor];
[elementB acceptVisitor:visitor];

当运行这段代码时,将会输出以下结果:

Visitor is operating on ElementA: ConcreteElementA operation
Visitor is operating on ElementB: ConcreteElementB operation

通过使用访问者模式,我们可以将元素和操作进行解耦,使得新增操作时不需要修改元素的代码。这提高了代码的可扩展性和可维护性。

需要注意的是,访问者模式在iOS开发中并不是一个经常使用的模式,它通常用于处理复杂的对象结构和操作。在简单的情况下,使用其他更简单的设计模式可能更加合适。使用访问者模式需要权衡代码的复杂性和可维护性,确保它能够带来实际的好处。

项目Demo

image.png

24-Design-Patterns

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYtQuOdz' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片