设计模式之(六)命令模式

前言

什么是命令模式

命令模式 属于行为型模式,在百度百科中的定义如下

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)

模式结构

Command:

定义命令的接口,声明执行的方法。

ConcreteCommand:

命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

Receiver:

接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

Invoker:

要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

Client:

创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。

模式分析

  • 命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。
  • 每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
  • 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
  • 命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
  • 命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

代码

场景

在当前控制器中心创建一个红色view,增加三个按钮,分别是放大,缩小,回退。对应的红色view就会放大,缩小,回退。

ViewController

创建ViewController类,显示redView,并有三个按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#import "ViewController.h"
#import "Receiver.h"
#import "reduceCommand.h"
#import "Invoker.h"
#import "amplifyCommand.h"
#import "CommandProtocol.h"
#define kccWidth [UIScreen mainScreen].bounds.size.width;


@interface ViewController ()
@property (strong, nonatomic) UIView * redView;
@property (strong, nonatomic) Receiver * receiver;
@end


@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

CGPoint centerPoint = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
self.redView = [[UIView alloc]initWithFrame:CGRectMake(centerPoint.x - 50/2,centerPoint.y - 50/2, 50, 50)];
self.redView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.redView];

self.receiver = [[Receiver alloc]init];
self.receiver.redView = self.redView;
self.receiver.width = 50;
}



/// 扩大
/// @param sender 扩大按钮
- (IBAction)amplify:(UIButton *)sender {

amplifyCommand *command = [[amplifyCommand alloc]initWithReceiver:self.receiver paramter:10];
[[Invoker sharedInstance] addAndExcute:command];
}
/// 缩小
/// @param sender 缩小按钮
- (IBAction)reduce:(UIButton *)sender {
reduceCommand *command = [[reduceCommand alloc]initWithReceiver:self.receiver paramter:10];
[[Invoker sharedInstance] addAndExcute:command];

}

/// 回退
/// @param sender 回退按钮
- (IBAction)rollback:(UIButton *)sender {
// 回退操作
[[Invoker sharedInstance] rollBack];

}

@end

接收者Receiver

负责具体实现的细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#import <UIKit/UIKit.h>


@interface Receiver : NSObject
@property (assign, nonatomic) CGFloat width;
@property (strong, nonatomic) UIView * redView;

/// 扩大view
/// @param pamameter 边长
-(void)amplifyView:(CGFloat)pamameter;

/// 缩小view
/// @param pamameter 边长
-(void)reduceView:(CGFloat)pamameter;

@end


#import "Receiver.h"
#define centerX [UIScreen mainScreen].bounds.size.width/2
#define centerY [UIScreen mainScreen].bounds.size.height/2
@implementation Receiver


-(void)amplifyView:(CGFloat)pamameter{
_width += pamameter;
_width = MIN(_width, [UIScreen mainScreen].bounds.size.width);
_redView.frame = CGRectMake(centerX - _width/2,centerY - _width/2, _width, _width);
}

-(void)reduceView:(CGFloat)pamameter{
_width -= pamameter;
_width = MAX(_width, 30);
_redView.frame = CGRectMake(centerX - _width/2,centerY - _width/2, _width, _width);
}

@end

接口命令CommandProtocol

1
2
3
4
5
6
7
8
9
10
11
#import <Foundation/Foundation.h>


@protocol CommandProtocol <NSObject>
/// 执行命令
- (void)excute;

/// 撤销命令
- (void)backExcute;

@end

具体的命令

amplifyCommand 放大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#import <Foundation/Foundation.h>
#import "CommandProtocol.h"
#import "Receiver.h"


@interface amplifyCommand : NSObject<CommandProtocol>
// 绑定接收器
- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter;
@end

#import "amplifyCommand.h"

@interface amplifyCommand()
@property (nonatomic, strong) Receiver *receiver;
@property (nonatomic, assign) CGFloat paramter;
@end


@implementation amplifyCommand

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

self.receiver = receiver;
self.paramter = paramter;
}
return self;
}


// 执行命令
- (void)excute {
[self.receiver amplifyView:self.paramter];
}

// 撤销命令
- (void)backExcute {
[self.receiver reduceView:self.paramter];
}
@end

reduceCommand 缩小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#import <Foundation/Foundation.h>
#import "CommandProtocol.h"
#import "Receiver.h"

@interface reduceCommand : NSObject<CommandProtocol>
// 绑定接收器
- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter;
@end


#import "reduceCommand.h"

@interface reduceCommand()
@property (nonatomic, strong) Receiver *receiver;
@property (nonatomic, assign) CGFloat paramter;
@end


@implementation reduceCommand

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

self.receiver = receiver;
self.paramter = paramter;
}
return self;
}


// 执行命令
- (void)excute {
[self.receiver reduceView:self.paramter];
}

// 撤销命令
- (void)backExcute {
[self.receiver amplifyView:self.paramter];
}
@end

请求者 Receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#import <Foundation/Foundation.h>
#import "CommandProtocol.h"

@interface Invoker : NSObject

+ (instancetype)sharedInstance;

// 回退指令
- (void)rollBack;

// 添加操作指令
- (void)addAndExcute:(id <CommandProtocol>)command;

@end


#import "Invoker.h"

@interface Invoker()
@property (strong, nonatomic) NSMutableArray *mArr; //存储操作指令的数组

@end

@implementation Invoker


+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
static Invoker *cls = nil;
dispatch_once(&onceToken, ^{
cls = [[[self class] alloc] init];
cls.mArr = [[NSMutableArray alloc]init];
});
return cls;
}

- (void)rollBack{
// 1.获取数组中的最后一个操作
id <CommandProtocol> command = self.mArr.lastObject;

// 2.操作调用,撤销的步骤
[command backExcute];

// 3.删除最后操作
[self.mArr removeLastObject];
}

// 添加操作指令
- (void)addAndExcute:(id <CommandProtocol>)command {
// 1.把操作添加到数组
[self.mArr addObject:command];

// 2.让操作调用实现的协议方法
[command excute];
}

@end

模式优点

  • 降低对象之间的耦合度。
  • 新的命令可以很容易地加入到系统中。
  • 可以比较容易地设计一个组合命令。
  • 调用同一方法实现不同的功能

模式缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

Demo地址