定义
将请求封装
成自己的对象,这可以让你使用不同的请求,队列,或者日志请求来参数化其他对象.命令模式也可以支持撤销操作.
要点
- 命令模式将发出请求的对象和执行请求的对象
解耦
. - 在被解耦的两者之间是通过命令对象进行沟通的.命令对象
封装
了接收者和一个或一组的动作. - 调用者通过调用命令对象的
excute()
发出请求,这会使得接收者的动作被调用. - 调用者可以接收命令当参数,甚至在运行时动态的执行.
- 命令可以撤销,用一个方法实现
excute()
之前的状态 - 宏命令允许调用多个命令.宏方法也可以支持传销.
个人理解
通过封装
我们在调用的对象根本不会关心事情是如何实行的,通过方法的调用我们也能能做一些很巧妙的事情,例如恢复到以前的状态,比如app退出到后台后如何记录以及快速恢复上次的执行.线程池,网络请求队列等等都可以这么操作.
解耦
之后我们可以很方便的在执行命令的对象中进行扩展
,提升了代码的可维护性.
我个人认为,命令模式的实现和适配器模式
很相似,都是间接的去实现调用,可能目前理解还不到位吧.
需求
书中有一个需求,设计一个遥控器来支持多种家电开关,要求方便扩展,具备撤销功能,下面来试着用OC实现一下书中的需求.
文件结构介绍
Base
文件夹下有两个类
,一个是Command
的基类,一个是电器类
的基类;Command
文件夹下有五个类,分别对应着空命令,打开灯,关闭灯,打开电视,关闭电视;ElectricAppliance
文件夹对应着电器类;ViewController
可以当做一个可视化的遥控器.通过这个文件夹结构,我们可以很快速的扩展这个遥控器
.
代码分析
BaseCommand
类
这个基类提供了两个用来继承却并没有被实现的方法:excute
和undo
,除此以外暂时没有更多的作用了.
NoCommand
类
这个类是一个空
命令类,我们使用这个空对象
类可以做一些事情,例如给我们的遥控器赋值时不用在对命令是否为空进行判断了.
- (void)excute{
NSLog(@"don't do anything");
}
- (void)undo{
NSLog(@"don't do anything");
}
具体电器开关类的实现文件
我们可以看到,我们使用构造方法将电器
传入到命令中,这里并没有在基类中编写是担心以后的扩展性不好.这是一个灯光开启的命令,那么他的excute
方法就对应的开灯,undo
方法就对应的撤销操作,当然这里的undo
方法是可以扩展的,例如使用一个数组来记录
他的状态,从而回到任意时刻.其他的开关命令类其实也是类似的.
#import "LightOnCommand.h"
#import "Light.h"
@interface LightOnCommand ()
@property (nonatomic, strong)Light *light;
@end
@implementation LightOnCommand
- (instancetype)initWithLight:(Light *)light{
if (self = [super init]) {
_light = light;
}
return self;
}
- (void)excute{
[self.light on];
}
- (void)undo{
[self.light off];
}
@end
ViewController(遥控器)
实现文件
我们在遥控器中会建立三个属性分别来保存打开电器命令数组
,关闭电器命令数组
,上一个执行的命令
.首先我们会使用懒加载来加载空命令到两个数组
中,后面我们会通过p_createCommand
对空命令进行替换
.点击按钮就能够取到对应的命令
,并且在属性中记录下最后
执行的命令,方便进行恢复
操作,当然这个恢复操作也是可以扩展的.
#import "ViewController.h"
#import "NoCommand.h"
#import "LightOnCommand.h"
#import "LightOffCommand.h"
#import "TVOnCommand.h"
#import "TVOffCommand.h"
#import "TV.h"
#import "Light.h"
static const int SWITCH_COUNT = 2;
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *onCommandArray;
@property (nonatomic, strong) NSMutableArray *offCommandArray;
@property (nonatomic, strong) BaseCommand *lastCommand;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self p_createCommand];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - private
- (void)p_createCommand{
TV *tv = [TV new];
TVOnCommand *tvOnCommand = [[TVOnCommand alloc] initWithTV:tv];
TVOffCommand *tvOffCommand = [[TVOffCommand alloc] initWithTV:tv];
Light *light = [Light new];
LightOnCommand *lightOnCommand = [[LightOnCommand alloc] initWithLight:light];
LightOffCommand *lightOffCommand = [[LightOffCommand alloc] initWithLight:light];
self.onCommandArray[0] = lightOnCommand;
self.onCommandArray[1] = tvOnCommand;
self.offCommandArray[0] = lightOffCommand;
self.offCommandArray[1] = tvOffCommand;
}
#pragma mark - target
- (IBAction)didClickOpenLightBtn:(id)sender {
self.lastCommand = self.onCommandArray[0];
[self.lastCommand excute];
}
- (IBAction)didClickCloseLightBtn:(id)sender {
self.lastCommand = self.offCommandArray[0];
[self.lastCommand excute];
}
- (IBAction)didClickTurnOnTV:(id)sender {
self.lastCommand = self.onCommandArray[1];
[self.lastCommand excute];
}
- (IBAction)didClickTurnOffTV:(id)sender {
self.lastCommand = self.offCommandArray[1];
[self.lastCommand excute];
}
- (IBAction)didClickUndoBtn:(id)sender {
[self.lastCommand undo];
}
#pragma mark - set && get
- (NSMutableArray *)onCommandArray{
if (nil == _onCommandArray) {
_onCommandArray = [NSMutableArray array];
NoCommand *noCommand = [NoCommand new];
for (int i = 0; i < SWITCH_COUNT; i ++) {
[_onCommandArray addObject:noCommand];
}
}
return _onCommandArray;
}
- (NSMutableArray *)offCommandArray{
if (nil == _offCommandArray) {
_offCommandArray = [NSMutableArray array];
NoCommand *noCommand = [NoCommand new];
for (int i = 0; i < SWITCH_COUNT; i ++) {
[_offCommandArray addObject:noCommand];
}
}
return _offCommandArray;
}
@end