将“请求”封装成对象,以便使用不同的请求、队列、或者日志来参数化其他对象。命令模式也支持可撤销的操作。
每一位召唤师都有QWER4个技能,当我们按下键盘上(或其他外设)的Q按钮后,我们操控的召唤师就会释放Q技能进行攻击。整个过程可以使用命令模式来描述。命令模式可以将请求者与接收者之间解耦。
命令模式是在特定接收者上绑定一组动作来封装成一个请求(命令对象),而这个请求只暴露出一个execute
方法,当此方法被调用的时候,接收者就会负责进行这些动作。而对于外面来看,其他对象不知道究竟是哪个接收者进行了哪些动作,只知道如果调用了execute
方法,请求的目的就能达到。
命令模式还能实现撤销操作,也比较简单,只需让接收者实现命令的相反操作即可。
1. 简单的命令模式
@protocol Command <NSObject>
- (void)execute; // 执行命令
- (void)undo; // 撤销命令
@end
@implementation Skill_Q {
Summoner *_summoner;
}
- (instancetype)initWithSummoner:(Summoner *)summoner {
self = [super init];
if (self) {
_summoner = summoner;
}
return self;
}
- (void)execute {
[_summoner touchSkillWithNumber:@"Q"];
}
- (void)undo {
[_summoner cancelSkillWithNumber:@"Q"];
}
@end
外设类图
@interface OuterDevice : NSObject
@property (nonatomic, strong, readonly) NSMutableDictionary *commandMap;
- (void)buttonAddCommand:(id<Command>)command number:(NSString *)number;//给外设按钮增加命令
- (void)pressedButtonWithNumber:(NSString *)number;//按下按钮
- (void)undoPressedButtonWithNumber:(NSString *)number;//撤销按下按钮操作
@end
@implementation OuterDevice
- (instancetype)init {
self = [super init];
if (self) {
_commandMap = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)buttonAddCommand:(id<Command>)command number:(NSString *)number {
[_commandMap setObject:command forKey:number];
}
- (void)pressedButtonWithNumber:(NSString *)number {
id<Command> command = [_commandMap objectForKey:number];
[command execute];
}
- (void)undoPressedButtonWithNumber:(NSString *)number {
id<Command> command = [_commandMap objectForKey:number];
[command undo];
}
@end
可能你觉得接收者不一定要存在,可以让命令对象直接实现execute
方法。但是,这样的做法使得调用者与接收者之间的耦合程度增加,而且还不能将接收者当作参数传递给命令。所以,还是将命令对象设计的简单、傻瓜点更合适。
具体代码传送门
2. 队列请求
命令模式可以将一个接收者和一组动作打包,供不同的线程调用,我们可以利用这个特性来创建队列请求操作。比如在队列的一端添加各式各样的命令对象,而线程从队列的另一端中取出命令对象,调用其execute
方法便能完成相关任务。线程不必知道具体的命令是什么,这样就达到了解耦。
3. 日志
我们还可以利用命令模式将用户的动作记录到日志中,通过序列化把命令对象存储到磁盘中,一旦系统死机,重启后我们就可以将命令对象重新加载并执行,这样就使系统恢复到了上一次死机前的那一刻。
倾情告白:为交互对象之间的松耦合设计而努力!
关注微信公众号CodingArtist,可以第一时间得到文章更新通知! _
网友评论