可恶的考试月终于结束了:((((
项目Demo
本文代码言语为Objective-C
在上一篇文章中,咱们介绍了断构型形式,在这一篇文章中,咱们将来介绍行为型形式。
行为型形式和结构型形式、创立型形式一样,是规划形式中的首要分类。
前面咱们介绍到,创立型形式更重视的是目标创立的灵敏性和可保护性,结构型形式更重视的是体系结构的规划和安排。
而今天要介绍的行为型形式(Behavioral Patterns)重视的是目标之间的通讯和交互办法,以完结特定的行为和职责分配。
尽管行为型形式、结构型形式和创立型形式重视的方面不同,但它们之间也存在联系和互相影响。在实践运用中,这些形式往往会结合运用,以到达更好的规划和架构。例如,咱们能够运用创立型形式创立目标,并运用结构型形式安排和办理这些目标,最终运用行为型形式界说目标之间的交互办法。
0 常见的行为型形式
- 职责链形式(Chain of Responsibility Pattern)
- 调查者形式(Observer Pattern)
- 战略形式(Strategy Pattern)
- 模板办法形式(Template Method Pattern)
- 指令形式(Command Pattern)
- 解说器形式(Interpreter Pattern)
- 迭代器形式(Iterator Pattern)
- 中介者形式(Mediator Pattern)
- 备忘录形式(Memento Pattern)
- 状况形式(State Pattern)
- 拜访者形式(Visitor Pattern)
在iOS 的实践开发中,职责链形式,调查者形式,战略形式,模板办法形式 这四种形式比较常用。
1 职责链形式(Chain of Responsibility Pattern)
职责链形式将恳求沿着处理链传递,直到有一个处理者能够处理该恳求。在职责链形式中,每个处理者都有一个对下一个处理者的引证,构成一个链条。
这种形式的首要意图是解耦发送者和接纳者之间的联系。发送者不需求知道详细是哪个接纳者会处理恳求,而接纳者也不需求知道恳求的发送者是谁。 经过这种办法,职责链形式能够灵敏地增加、修正或许删去处理者,而不会对体系的其他部分产生影响。
职责链形式是在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
。详细的处理者(ConcreteHandlerA
、ConcreteHandlerB
和ConcreteHandlerC
)继承自笼统处理者,并完结了自己的处理逻辑。
客户端代码创立了详细处理者的实例,并经过设置 nextHandler
特点将它们链接在一起构成职责链。当一个恳求被发送给第一个处理者时,它会首要尝试处理恳求,假如不满意处理条件,则将恳求传递给下一个处理者,直到有一个处理者能够处理恳求或许到达链的结尾。
在上述代码中,客户端发送了三个恳求:5、15和25。依据详细处理者的处理条件,恳求被传递到了不同的处理者进行处理。输出成果如下:
ConcreteHandlerA 处理了恳求 5
ConcreteHandlerB 处理了恳求 15
ConcreteHandlerC 处理了恳求 25
在iOS 开发中,职责链形式一般运用于以下场景:
- 事情处理:当一个事情需求经过多个目标进行处理时,能够运用职责链形式。例如,iOS中的事情传递机制就能够看作是职责链形式的运用。事情首要被传递给视图层次结构中的顶层视图,然后逐级向下传递,直到找到能够处理该事情的视图。
- 过错处理:当在运用中发生过错时,能够运用职责链形式来处理过错。每个处理者能够依据过错的类型、严峻程度等条件来决议是否能够处理该过错。假如一个处理者无法处理该过错,将过错传递给下一个处理者,直到找到能够处理的处理者或许到达链的结尾。
- 恳求过滤和验证:在网络恳求或许数据处理中,能够运用职责链形式对恳求进行过滤和验证。每个处理者能够依据恳求的特定条件进行过滤和验证操作,例如验证恳求的合法性、验证用户权限等。
- 音讯传递和告诉处理:当需求将音讯或许告诉传递给多个目标时,能够运用职责链形式。每个处理者能够依据音讯的内容或许类型来决议是否处理该音讯,并进行相应的操作。
恳求过滤和验证比如
当涉及到网络恳求或数据处理时,能够运用职责链形式对恳求进行过滤和验证:
// 笼统处理者
@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
。详细处理者 RequestValidityHandler
、UserPermissionHandler
和 RequestProcessingHandler
分别完结了自己的恳求过滤和验证、用户权限验证以及恳求处理逻辑。
客户端代码创立了详细处理者的实例,并经过设置 nextHandler
特点将它们链接在一起构成职责链。当一个恳求被发送时,它首要经过恳求合法性验证处理者,然后是用户权限验证处理者,最终是恳求处理处理者。每个处理者依据恳求的特定条件进行过滤、验证或处理,并决议是否将恳求传递给下一个处理者。
打印输出:
恳求合法
用户权限验证经过
处理恳求中...
职责链形式具有以下长处:
- 解耦:职责链形式将发送者和接纳者解耦,发送者不需求知道详细的接纳者,而接纳者也不需求知道恳求的发送者,使体系的各个部分之间的耦合度下降。
- 灵敏性,可扩展性:职责链形式答应动态地增加、修正或删去处理者,能够依据实践需求灵敏地调整处理链的结构和次序,而不会影响其他部分的代码。
- 可保护性:每个处理者只重视自己的职责,使得代码愈加模块化和可保护。
职责链形式也有一些缺陷:
- 恳求的处理不保证被接纳:因为每个处理者都有或许处理或许传递恳求,因此不能保证恳求必定会被处理或许接纳者会被找到。假如职责链没有被正确地构建或许装备,恳求或许会被忽略。
- 功用影响:在职责链中,恳求需求经过多个处理者的顺次处理,或许会导致处理链较长,影响功用。
- 或许导致体系变杂乱:假如职责链的规划不当或许链条过长,或许会导致代码变得杂乱,下降代码的可读性和可保护性。
综上所述,职责链形式在解耦、灵敏性和可扩展性方面具有长处,但需求留意处理次序、功用和代码杂乱性等问题。
2 调查者形式(Observer Pattern)
调查者形式用于目标之间的一对多依靠联系。在该形式中,一个目标(称为主题或可调查者)保护一系列调查者目标,使得当主题状况发生改变时,一切调查者都会自动收到告诉并进行相应的更新。
而在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
布景颜色从原本的灰色变成蓝色,也打印输出”登录成功”。
经过运用NotificationCenter
,咱们将发布者(发送告诉的LoginViewController
)与订阅者(收到告诉的ProfileViewController
)解耦,它们之间不直接进行通讯。任何模块都能够注册对告诉的调查,然后完结了解耦和通讯。
这种办法在iOS 开发中广泛运用,比如在数据更新时发送告诉,让相关的UI 模块进行刷新,或许在运用程序状况发生改变时发送告诉,让其他模块作出相应的处理。调查者形式的运用能够大大简化不同模块之间的交互和通讯,提高代码的可保护性和可扩展性。
3 战略形式(Strategy Pattern)
战略形式经过界说不同的战略目标,并将其封装在详细的类中,能够完结在运转时动态地挑选不同的算法或行为。
战略形式将算法独立于运用它的客户端进行封装,使得算法能够独立于客户端的改变而改变。这种形式经过界说一系列可交换的算法族,并将每个算法封装起来,使它们能够相互替换。
比如
算法挑选:当需求依据不同的条件或用户输入挑选不同的算法时,能够运用战略形式。例如,关于一个计算器运用程序,能够依据用户挑选的运算符运用不同的算法来履行计算:
首要,咱们界说一个战略接口(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
经过运用战略形式,咱们能够依据用户挑选的不同运算符,动态地切换不同的战略,然后完结不同的计算操作。这种形式使得算法的改变独立于客户端,而且提高了代码的灵敏性和可保护性。
战略形式具有以下长处:
- 供给了更好的代码安排和结构:战略形式将每个详细战略类封装在单独的类中,使得代码结构清晰、可读性强。它将算法的完结与运用算法的客户端代码别离,下降了耦合性,使代码更易于保护和扩展。
- 供给了更高的灵敏性和可扩展性:战略形式使得能够在运转时动态地切换不同的战略,而不需求修正客户端代码。新的战略类能够很容易地增加到体系中,然后增加了体系的灵敏性和可扩展性。
- 使算法独立于客户端:战略形式将算法封装在战略类中,使得算法能够独立于客户端进行改变。客户端只需求知道怎么挑选和运用不同的战略,而无需了解详细战略的完结细节,提高了代码的笼统程度和可了解性。
- 促进了代码复用:经过界说通用的战略接口和多个详细战略类,能够促进代码的复用。不同的客户端能够共享相同的战略类,避免了代码的重复编写。
但是,战略形式也存在一些缺陷:
- 增加了类的数量:引进战略形式会增加类的数量,特别是当战略较多时,或许会导致类的爆炸性增长,增加了代码的杂乱性。
- 客户端需求了解不同的战略:尽管客户端代码与详细战略的完结别离,但客户端依然需求了解不同战略的存在和挑选。
- 战略的挑选和切换开支:在运转时挑选不同的战略会引进必定的开支。假如战略的挑选频繁发生改变,或许会影响体系的功用。
4 模板办法形式(Template Method Pattern)
模板办法形式界说了一个操作中的算法的骨架,将一些过程延迟到子类中完结。模板办法答应子类在不改变算法结构的状况下重新界说算法的某些过程。
在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
和两个笼统办法primitiveOperation1
和primitiveOperation2
。子类ConcreteClass1
和ConcreteClass2
继承了AbstractClass
,并完结了笼统办法。在运用示例中,咱们能够看到不同子类的详细完结过程在模板办法中被调用。这样,模板办法供给了算法的骨架,而详细完结由子类完结。
在iOS开发中,模板办法形式常常用于以下场景:
-
UIViewController的生命周期办法:在iOS开发中,
UIViewController
是一个常用的基类,用于办理视图控制器的生命周期和视图的显现。UIViewController
供给了一系列生命周期办法(例如viewDidLoad
、viewWillAppear
、viewWillDisappear
等),开发者能够重写这些办法以增加自界说逻辑。这里的生命周期办法能够看作是模板办法,界说了视图控制器的全体行为骨架,而详细的完结能够由子类重写。 -
UITableView和UICollectionView的数据源办法:
UITableView
和UICollectionView
是iOS 开发中常用的用于显现列表和网格的视图控件。它们运用数据源协议(UITableViewDataSource
和UICollectionViewDataSource
)来供给数据和装备单元格。这些数据源办法(例如numberOfSectionsInTableView
、numberOfRowsInSection
、cellForRowAtIndexPath
等)能够看作是模板办法,界说了列表或网格的全体结构,而详细的完结由数据源目标供给。
在这些场景中,模板办法形式能够供给一个结构或许约定,使得基类或协议界说了全体行为的骨架,而详细的完结能够由子类或目标来供给。这种形式能够提高代码的可重用性和可保护性,一起也能够供给共同的编程接口和约束,方便开发者进行扩展和定制。
模板办法形式也有一些缺陷:
- 约束了部分自由度:模板办法形式在界说算法骨架时,将一些过程固定下来,子类只能重写或扩展指定的办法。
- 增加了类的个数:运用模板办法形式需求界说基类和多个子类,这样会增加类的个数和代码量。假如算法的改变较小或许只有少量几个子类需求不同的完结,运用模板办法形式或许会显得过于繁琐。
- 难以控制子类的行为:在模板办法形式中,子类能够经过重写办法来完结特定过程的定制,这也意味着子类或许会对算法的全体行为产生影响。
5 指令形式(Command Pattern)
指令形式用于将恳求(指令)封装成一个目标,然后答应依据不同的恳求参数来参数化客户端目标。经过运用指令形式,能够将办法调用、恳求或操作封装到单个目标中,使得咱们能够将这些指令行列、记载日志、吊销操作或进行重做等。
在指令形式中,有四个中心组件:
- 指令(Command):指令目标封装了对特定操作的恳求。它一般包括履行操作的办法。
- 发送者(Invoker):发送者是一个目标,它知道怎么触发指令来履行特定的操作。它将指令目标与接纳者目标解耦。
- 接纳者(Receiver):接纳者是实践履行操作的目标。指令形式经过将指令与接纳者别离,使得能够独登时改变接纳者或指令,而不需求修正发送者。
- 客户端(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开发中,指令形式能够在以下几个常见场景中运用:
- 吊销和重做操作:指令形式能够用于完结吊销和重做操作。经过将操作封装成指令目标,能够将每个操作保存在一个历史记载中,并在需求时依照次序履行或吊销。这在图形编辑器、文本编辑器等运用程序中十分有用。
- 动作菜单和工具栏:指令形式能够用于完结动作菜单和工具栏。每个菜单项或工具栏按钮能够相关一个指令目标,并在触发时履行相应的指令。这种办法使得用户界面的动作和操作与实践的指令目标解耦,使得用户界面愈加灵敏和可扩展。
- 异步使命行列:指令形式能够用于办理异步使命行列。每个指令目标能够代表一个需求履行的异步使命,将使命的履行封装在指令目标中。经过运用指令形式,能够完结使命行列的办理、优先级控制、使命吊销等功用。
异步使命行列
指令形式办理异步使命行列,完结增加、删去和履行使命,能够完结使命的优先级控制,也能够支撑使命的暂停。
异步使命指令类:
@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
办法,能够履行使命行列中的一切使命,并依据使命的优先级进行排序和履行。还增加了 pauseTasks
和 resumeTasks
办法来暂停和康复使命的履行。在示例代码中,咱们演示了增加、删去、履行使命,以及使命的暂停和康复的功用。
经过运用指令形式办理异步使命行列,咱们能够方便地增加、删去和履行使命,能够依据使命的优先级控制使命的履行次序,而且能够支撑使命的暂停和康复操作。
6 解说器形式(Interpreter Pattern)
解说器形式(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
和非终结符表达式 AdditionExpression
、SubtractionExpression
。上下文 Context
包括了表达式解说所需的大局信息。在客户端代码中,咱们创立了一个算术表达式,并运用上下文中的变量进行解说和计算,最终输出计算成果。
但是也能够看到,类型查看和类型转换十分繁琐,假如运用Swift 这样的言语,或许会得到改进,但事实上,在iOS 实践开发中,解说器形式确实比较少用到,它一般在需求构建一种特定言语的解说器或许需求解析杂乱的表达式时运用。
7 迭代器形式(Iterator Pattern)
迭代器形式供给了一种拜访调集目标元素的办法,而无需露出调集的内部表明。经过运用迭代器形式,能够在不露出调集内部结构的状况下,按次序访问调会集的元素。
一般,能够运用迭代器形式来遍历调集目标(如数组、字典等)。
比如
// 迭代器接口
@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)
中介者形式经过封装一系列目标之间的交互,将目标之间的通讯转变为经过中介者进行的会集式通讯。中介者形式的目标是削减目标之间的直接耦合,经过引进一个中介者目标,使得目标之间的交互愈加灵敏、可保护和可扩展。
在中介者形式中,各个目标不再直接相互通讯,而是经过中介者进行通讯。当一个目标发生改变时,它不需求知道详细需求告诉哪些目标,而是将音讯发送给中介者,由中介者来处理告诉其他相关目标。这样,目标之间的耦合度下降,它们只需求和中介者进行通讯,而不需求了解其他目标的详细细节。
比如
现在有两个搭档需求经过中介者告诉:
// 中介者接口
@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:
办法。ConcreteColleague1
和ConcreteColleague2
是详细的搭档类,继承自笼统搭档类,分别完结了详细的发送和接纳行为。
在运用示例中,创立了一个详细的中介者目标mediator
,以及两个详细的搭档目标colleague1
和colleague2
。经过设置中介者的搭档目标,并经过调用搭档目标的send:
办法发送音讯,触发中介者目标的音讯传递过程。
当colleague1
发送音讯时,中介者目标mediator
将音讯传递给colleague2
,colleague2
收到音讯后打印出来。同样地,当colleague2
发送音讯时,中介者目标mediator
将音讯传递给colleague1
,colleague1
收到音讯后打印出来。
这样,经过中介者形式,目标之间的通讯经过中介者进行会集处理,完结了目标之间的解耦。
在iOS开发中,中介者形式常常运用于以下场景:
-
视图控制器之间的通讯:在iOS运用程序中,
ViewController
之间需求进行数据传递和交互。运用中介者形式能够将这些通讯逻辑笼统到一个中介者目标中,视图控制器只需求与中介者进行通讯,而无需直接依靠其他视图控制器。这样能够下降视图控制器之间的耦合度,使代码愈加清晰和可保护。 - 多个模块之间的通讯:在大型iOS运用程序中,一般由多个模块或组件组成,它们之间需求进行数据传递和交互。运用中介者形式能够引进一个中介者目标,用于会集处理模块之间的通讯。 模块只需求与中介者进行通讯,而无需了解其他模块的详细细节,然后完结模块之间的解耦和灵敏性。
从上面的运用也能够看到,中介者形式有削减目标之间耦合性,增加复用性和扩展性和会集控制交互逻辑的长处。一起,中介者形式也或许存在中介目标随着通讯的目标变多而变的杂乱,臃肿,难以保护,还有增加体系的杂乱性的缺陷。
9 备忘录形式(Memento Pattern)
备忘录形式(Memento Pattern)用于在不损坏封装性的前提下捕获和康复目标的内部状况。该形式经过创立一个备忘录目标来存储目标的状况,并将其保存在原始目标之外,以便在需求时康复状况。
在备忘录形式中,一般涉及三个首要人物:
- 发起人(Originator):它是需求保存状况的目标。它能够创立一个备忘录来保存当时状况,也能够运用备忘录来康复之前保存的状况。
- 备忘录(Memento):它是保存发起人目标状况的目标。它一般具有能够获取发起人状况的办法,以及设置发起人状况的办法。
- 办理者(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
来保存多个备忘录目标,并供给了addMemento
和getMementoAtIndex
办法来增加和获取备忘录。
在运用备忘录形式时,发起人能够创立备忘录目标并将其交给办理者保存。假如需求康复之前的状况,能够从办理者那里获取相应的备忘录目标,并经过发起人目标康复状况。这样能够在不损坏封装性的状况下完结状况的保存和康复。
备忘录形式在iOS开发中常用于需求保存和康复目标状况的场景,尤其是在吊销和康复功用、数据持久化、界面状况办理以及游戏状况办理等方面。经过运用备忘录形式,能够更好地控制目标的状况,并供给灵敏的状况办理机制。
能够看出,备忘录形式具有状况保存和康复的别离,支撑吊销和康复的长处,但一起,备忘录形式也存在内存耗费过大,会露出目标原始特点的缺陷。
10 状况形式(State Pattern)
状况形式(State Pattern)答应对象在内部状况改变时改变其行为,使其看起来像是修正了本身的类。状况形式经过将状况封装成独立的类,使得状况的改变不直接影响到目标的行为,然后完结了状况与行为的解耦。
在状况形式中,目标依据内部状况的不同而改变其行为。它包括以下首要人物:
- Context(上下文):界说客户端感兴趣的接口,并保护一个详细状况类的实例,这个实例界说当时状况。
-
State(状况):界说一个接口,用于封装与
Context
的特定状况相关的行为。 -
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
和三个详细状况类PlayState
、PauseState
和StopState
,它们完结了状况接口State
。在MusicPlayer
中,咱们经过保护一个currentState
实例变量来跟踪当时状况,而且在状况改变时更新它。客户端代码经过调用playMusic
、pauseMusic
和stopMusic
等办法来操作音乐播映器,详细的行为由当时状况目标处理。
这样,当咱们需求增加新的播映状况时,只需创立一个新的详细状况类并完结相应的行为办法,而不需求修正MusicPlayer
类的代码,完结了状况和行为的解耦。
当然,上面这个比如仅仅协助了解状况形式,在iOS 实践开发中,状况形式适用于目标有多个状况且状况之间有杂乱的转换逻辑的状况。(比如:视图控制器生命周期办理:视图控制器在其生命周期中阅历多个状况,如viewDidLoad
、viewWillAppear
、viewDidAppear
、viewWillDisappear
等。能够运用状况形式来办理这些状况,并依据不同的状况履行相应的操作,以完结杰出的生命周期办理)它能够使代码愈加模块化、灵敏和可扩展。但在简略的场景中,引进状况形式或许会过于杂乱,不切实践。
11 拜访者形式(Visitor Pattern)
拜访者形式(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
然后,咱们界说两个详细元素类 ConcreteElementA
和 ConcreteElementB
,它们完结了 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
24-Design-Patterns