Cowboy Tech

iOS设计模式-命令

电视机、遥控器与接收器之间的关系

  1. 遥控器与接收器并非必要的设备,比如手动调节电视机的音量也可以
  2. 接收器转换遥控器的信号

命令模式

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)。命令的执行者和命令的发起者是解耦的,比如最后一个实例中的command的和view是没有直接关联,命令模式能执行回退,撤销,重做等操作,这是最大的用途

  1. Client:可以有多个实体,client即使没有receiver,自身也能操作。
  2. Receiver: 持有了client的引用,将遥控器的指令翻译成电视机能接收的,它的方法就是针对client的操作。
  3. CommandProtocol:所有对象必须遵循的规范。
  4. Command: 每个命令抽象成一个对象,且必须遵循命令协议。好比遥控器上的每个按钮。
  5. Invoker:单例,存储命令的地方。好比一个含有很多按钮的遥控器。

CommandProtocol

@protocol CommandProtocol <NSObject>

@required

//命令的执行
- (void)excute;

//与接收器绑定并设置参数
- (void)initWithReceiver:(Receiver *)receiver paramter:(id)paramter;

Invoker :

@interface Invoker : NSObject

//回退操作
- (void)rollBack;

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

@interface Invoker ()

@property (nonatomic, strong) NSMutableArray  *queue;
@end

@implementation Invoker

+ (instancetype)shareInstance {

static Invoker *shareInstanceValue = nil;
static dispatch_once_t oncePredicate;

dispatch_once(&oncePredicate, ^{

    shareInstanceValue       = [[Invoker alloc] init];
    shareInstanceValue.queue = [NSMutableArray array];
});

return shareInstanceValue;
}

- (void)rollBack {

// todo
}

- (void)addAndExcute:(id <CommandProtocol>)command {

NSParameterAssert(command);

[self.queue addObject:command];
[command excute];
}

Receiver

@interface Receiver : NSObject
//被服务对象
@property (nonatomic, strong) id client;
//增加频道
- (void)addNum:(NSNumber *)num;
//减少频道
- (void)delNum:(NSNumber *)num;

@end

@implementation Receiver

- (void)addNum:(NSNumber *)num {

// todo
}

- (void)delNum:(NSNumber *)num {

// todo
}
@end

改变一个视图的明暗程度

ViewController

typedef enum : NSUInteger {

kAddButtonTag = 10, // 增加按钮枚举值
kDelButtonTag,      // 减少按钮枚举值
kRolButtonTag,      // 回退按钮枚举值

} ViewControllerEnumValue;

@interface ViewController ()

@property (nonatomic, strong) UIButton *addButton;
@property (nonatomic, strong) UIButton *delButton;
@property (nonatomic, strong) UIButton *rolButton;
@property (nonatomic, strong) Receiver *receiver;

@end

@implementation ViewController

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

// 接收器
self.receiver            = [[Receiver alloc] init];
self.receiver.clientView = self.view;

// 初始化按钮
[self initButtons];
}

- (void)buttonsEvent:(UIButton *)button {

if (button.tag == kAddButtonTag) {

    NSLog(@"增加操作");
    LighterCommond *commond = [[LighterCommond alloc] initWithReceiver:self.receiver paramter:0.1];
    [[Invoker shareInstance] addAndExcute:commond];

} else if (button.tag == kDelButtonTag) {

    NSLog(@"减少操作");
    DarkerCommand *commond = [[DarkerCommand alloc] initWithReceiver:self.receiver paramter:0.1];
    [[Invoker shareInstance] addAndExcute:commond];

} else if (button.tag == kRolButtonTag) {

    NSLog(@"回退操作");
    [[Invoker shareInstance] rollBack];
}
}

#pragma mark - 无关初始化
- (void)initButtons {

// delButton
self.delButton     = [[UIButton alloc] initWithFrame:CGRectMake(10, 25, 30, 30)];

self.delButton.tag               = kDelButtonTag;
self.delButton.layer.borderWidth = 1.f;

[self.delButton setTitle:@"-"
                forState:UIControlStateNormal];

[self.delButton setTitleColor:[UIColor redColor]
                     forState:UIControlStateNormal];

[self.delButton addTarget:self
                   action:@selector(buttonsEvent:)
         forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:self.delButton];

// addButton
self.addButton     = [[UIButton alloc] initWithFrame:CGRectMake(10 + 40, 25, 30, 30)];

self.addButton.tag               = kAddButtonTag;
self.addButton.layer.borderWidth = 1.f;

[self.addButton setTitle:@"+"
                forState:UIControlStateNormal];

[self.addButton setTitleColor:[UIColor redColor]
                     forState:UIControlStateNormal];

[self.addButton addTarget:self
                   action:@selector(buttonsEvent:)
         forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:self.addButton];

// rolButton
self.rolButton     = [[UIButton alloc] initWithFrame:CGRectMake(10 + 80, 25, 90, 30)];

self.rolButton.tag               = kRolButtonTag;
self.rolButton.layer.borderWidth = 1.f;

[self.rolButton setTitle:@"rollBack"
                forState:UIControlStateNormal];

[self.rolButton setTitleColor:[UIColor redColor]
                     forState:UIControlStateNormal];

[self.rolButton addTarget:self
                   action:@selector(buttonsEvent:)
         forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:self.rolButton];
}

CommandProtocol

@protocol CommandProtocol <NSObject>

@required

//执行命令
- (void)excute;
//撤销命令
- (void)rollBackExcute;

@end

DarkerCommand

@interface DarkerCommand : NSObject <CommandProtocol>
//与接收器绑定并设置参数
- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter;

@end

@interface DarkerCommand ()

@property (nonatomic, weak) Receiver *receiver;
@property (nonatomic)       CGFloat   paramter;

@end

@implementation DarkerCommand

- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter {

self = [super init];
if (self) {

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

return self;
}

- (void)excute {

[self.receiver makeDarker:self.paramter];
}

- (void)rollBackExcute {

[self.receiver makeLighter:self.paramter];
}

@end

LighterCommond

@interface LighterCommond : NSObject <CommandProtocol>

//与接收器绑定并设置参数
- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter;
@end


@interface LighterCommond ()

@property (nonatomic, weak) Receiver *receiver;
@property (nonatomic)       CGFloat   paramter;

@end

@implementation LighterCommond

- (instancetype)initWithReceiver:(Receiver *)receiver paramter:(CGFloat)paramter {

    self = [super init];
    if (self) {

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

    return self;
}

- (void)excute {

    [self.receiver makeLighter:self.paramter];
}

- (void)rollBackExcute {

    [self.receiver makeDarker:self.paramter];
}
@end

Invoker

@interface Invoker : NSObject

+ (instancetype)shareInstance;
//回退操作
- (void)rollBack;

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

@end

@interface Invoker ()

@property (nonatomic, strong) NSMutableArray  *queue;

@end

@implementation Invoker

+ (instancetype)shareInstance {

static Invoker *shareInstanceValue = nil;
static dispatch_once_t oncePredicate;

dispatch_once(&oncePredicate, ^{

    shareInstanceValue       = [[Invoker alloc] init];
    shareInstanceValue.queue = [NSMutableArray array];
});

return shareInstanceValue;
}

- (void)rollBack {

id <CommandProtocol> command = self.queue.lastObject;
[command rollBackExcute];

[self.queue removeLastObject];
}

- (void)addAndExcute:(id <CommandProtocol>)command {

NSParameterAssert(command);

[self.queue addObject:command];
[command excute];
}

@end

Receiver

@interface Receiver : NSObject {

CGFloat _hud;
CGFloat _saturation;
CGFloat _brightness;
CGFloat _alpha;
}

//被服务对象
@property (nonatomic, weak) UIView  *clientView;
//变暗
- (void)makeDarker:(CGFloat)pamameter;
//变亮
- (void)makeLighter:(CGFloat)pamameter;
@end

@implementation Receiver

//根据传入的UIView,获取各个维度的值

- (void)setClientView:(UIView *)clientView {

_clientView    = clientView;
UIColor *color = _clientView.backgroundColor;

//获取背景色各个维度的值
[color getHue:&_hud
   saturation:&_saturation
   brightness:&_brightness
        alpha:&_alpha];
    }

    - (void)makeDarker:(CGFloat)pamameter {

_brightness -= pamameter;
_brightness  = MAX(0, _brightness);

_clientView.backgroundColor = [UIColor colorWithHue:_hud
                                         saturation:_saturation
                                         brightness:_brightness
                                              alpha:_alpha];
                                          }

- (void)makeLighter:(CGFloat)pamameter {

_brightness += pamameter;
_brightness  = MIN(1, _brightness);

_clientView.backgroundColor = [UIColor colorWithHue:_hud
                                         saturation:_saturation
                                         brightness:_brightness
                                              alpha:_alpha];
                                          }

@end

Tag for Button

通过设定tag值,来调用相对应的button

typedef enum : NSUInteger {

kAddButtonTag = 10, // 增加按钮枚举值
kDelButtonTag,      // 减少按钮枚举值
kRolButtonTag,      // 回退按钮枚举值

} ViewControllerEnumValue;

..........

self.delButton.tag = kDelButtonTag;
self.addButton.tag = kAddButtonTag;
self.rolButton.tag = kRolButtonTag;

..........

if (button.tag == kAddButtonTag) {


} else if (button.tag == kDelButtonTag) {


} else if (button.tag == kRolButtonTag) {


}

协议

协议就是抽象归纳出某一类对象共同拥有的特性