Objective-C设计模式解析-命令

看图识模式

前提&场景

咱们有若干个军团,军团有若干功能:1.陆地攻击目标 2.轰炸目标 3.保护目标 4.从目标撤退等。
将军能够下达指令给军团,调用军团的功能。编程

需求

在一场battle中,将军指挥若定,给旗下的军团下达了一系列的命令:
攻击1号山地
攻击4号山地
保护3号山地
......atom

若是用类去实现,能够这样spa

clipboard.png

将军直接调用军团A的attack:方法,一样也能够直接调用bomb、protect方法,这样并无问题。设计

若是咱们引入新的需求:3d

咱们须要对将军下发的命令进行排队(下发多条命令时须要按照优先级执行)、记录执行命令、撤销命令、传递命令、重复执行等相关操做。日志

若是仍是按照上面直接调用来作,基本上很难实现,有如下几个主要问题:code

  • 不一样军种攻击方法不一样,须要知道每一个类或方法的细节才能调用。致使彼此拥有高耦合度。对象

    • 将军下命令攻击某个标的,本质上他并不关心由那个军种该如何攻击的这种细节。
  • 对命令进行操做(撤销、记录等)将很是复杂blog

    • 直接调用的后果就是不能直接找到某次调用,再对此次操做进行操做。一般须要额外的环境存放这些操做相关的参数、顺序、执行者等,当程序变大时,基本上难以维护。
  • 命令不可传递和复用排序

    • 若是须要多个军团攻击目标A,须要调用每一个军团攻击方法。某个时间重复攻击,任然须要再次调用,不能复用。

如何设计呢?

这里主要解决2个问题
  • 须要把命令封装起来,这样咱们就能够对这个封装的对象进行种种操做了

    好比将军下的命令是一封信,信里上标明执行的等级,攻击的目标等参数信息。
    当若干封信来的时候咱们能够根据等级排列,执行完后还能够把信交给其它军团再次执行。。。
  • 调用者和执行者解耦

    将军(调用者)封装一个命令对象,哪一个军团接到这个命令对象就执行这个命令,调用者不用关心执行的细节,只需关注命令自己便可。

以下图:

clipboard.png

模式定义

定义

命令模式: 将请求封装为一个对象,从而可用不一样的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操做。

静态类图

clipboard.png

命令模式包含以下角色:
  • 客户端(Client)角色: 建立一个具体命令(ConcreteCommand)对象并肯定其接收者。
  • 命令(Command)角色:声明了一个给全部具体命令类的抽象接口。
  • 具体命令(ConcreteCommand)角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操做。execute()方法一般叫作执行方法。
  • 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫作行动方法。
  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类均可以成为接收者,实施和执行请求的方法叫作行动方法。

分析

命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。

  • 每个命令都是一个操做:请求的一方发出请求,要求执行一个操做;接收的一方收到请求,并执行操做。
  • 命令模式容许请求的一方和接收的一方独立开来,使得请求的一方没必要知道接收请求的一方的接口,更没必要知道请求是怎么被接收,以及操做是否被执行、什么时候被执行,以及是怎么被执行的。
  • 命令模式使请求自己成为一个对象,这个对象和其余对象同样能够被存储和传递。
  • 命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

动机

在软件设计中,咱们常常须要向某些对象发送请求,可是并不知道请求的接收者是谁,也不知道被请求的操做是哪一个,咱们只需在程序运行时指定具体的请求接收者便可,此时,可使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

命令模式能够对发送者和接收者彻底解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只须要知道如何发送请求,而没必要知道如何完成请求。这就是命令模式的模式动机。

Command模式是最让我疑惑的一个模式。它实际上不是个很具体,规定不少的模式,正是这个灵活性,让人容易产生confuse。

代码

根据上面的场景来进行code实现;

接收者

接受者是命令模式中去执行具体操做的对象,这里指的具体军团

@interface Legion : NSObject

@property (nonatomic, copy) NSString *name;

- (instancetype)initWithName: (NSString *)name;

// 攻击
- (void)attack:(NSString *)target;
// 轰炸
- (void)bomb:(NSString *)target;
// 保护
- (void)protect:(NSString *)target;
// 撤回
- (void)recall:(NSString *)target;

@end

@implementation Legion

- (instancetype)initWithName: (NSString *)name
{
    self = [super init];
    if (self) {
        _name = name;
    }
    return self;
}

- (void)attack:(NSString *)target
{
    NSLog(@"军团: %@--------攻击: %@", self.name, target);
}

- (void)bomb:(NSString *)target
{
    NSLog(@"军团: %@--------轰炸: %@", self.name, target);
}

- (void)protect:(NSString *)target
{
    NSLog(@"军团: %@--------保护: %@", self.name, target);
}

- (void)recall:(NSString *)target
{
    NSLog(@"军团: %@--------撤回: %@", self.name, target);
}

@end

抽象命令Command

命令对象中暴露了指定的调用execute方法,命令的调用者和接收者针对抽象的命令类编写,从而达到解耦合的目的。
发起命令的对象彻底不知道具体实现对象是谁,也不知道如何实现。

#import "Legion.h"

@interface Command : NSObject

@property (nonatomic, assign) NSInteger level;
@property (nonatomic, strong) Legion *legion;
@property (nonatomic, copy) NSString *target;

- (instancetype)initWithReciver:(Legion *)legion target:(NSString *)target;

- (void)attack;

- (void)recall;

@end

@implementation Command

- (instancetype)initWithReciver:(Legion *)legion target:(NSString *)target
{
    self = [super init];
    if (self) {
        _legion = legion;
        _target = target;
    }
    return self;
}

- (void)attack
{
    
}

- (void)recall
{
    
}

@end

具体命令

陆地攻击命令 LandAttackCommand
#import "Command.h"

@interface LandAttackCommand : Command

- (void)attack;
- (void)recall;

@end

@implementation LandAttackCommand

- (void)attack
{
    [self.legion attack:self.target];
}

- (void)recall
{
    [self.legion recall:self.target];
}

@end
空袭命令 AirAttackCommand
#import "Command.h"

@interface AirAttackCommand : Command

- (void)attack;
- (void)recall;

@end

@implementation AirAttackCommand

- (void)attack
{
    [self.legion bomb:self.target];
}

- (void)recall
{
    [self.legion recall:self.target];
}

@end

调用者&客户端

这里咱们能够写一个调用者类,用这个类负责命令的建立和调用,还有对命令的管理(好比排序、日志记录等等)。
这里我就不单独写了,具体业务中能够依据具体状况去编写。
下面的调用例子把客户端和调用者放在一块儿不作具体区分。

Legion *legionA = [[Legion alloc] initWithName:@"军团A"];
        
        Command *landComand1 = [[LandAttackCommand alloc] initWithReciver:legionA target:@"4号高地"];
        landComand1.level = 100;
        Command *landComand2 = [[LandAttackCommand alloc] initWithReciver:legionA target:@"5号高地"];
        landComand2.level = 200;
        Command *airAttackCommand1 = [[AirAttackCommand alloc] initWithReciver:legionA target:@"4号高地"];
        airAttackCommand1.level = 50;
        Command *airAttackCommand2 = [[AirAttackCommand alloc] initWithReciver:legionA target:@"5号高地"];
        airAttackCommand2.level = 500;
        
        NSArray *commandList = @[landComand1, landComand2, airAttackCommand1, airAttackCommand2];
        for (Command *comand in commandList) {
            [comand attack];
        }
        
        // 命令传递
        Legion *legionB = [[Legion alloc] initWithName:@"军团B"];
        landComand1.legion = legionB;
        [landComand1 attack];
        
        // 撤销
        [landComand1 recall];
        
        // 能够根据level等级对全部的对命令进行排序、记录等操做...

结果

clipboard.png

因而可知,咱们对请求行为进行了封装,从而能够对这个请求对象(命令)进行一系列的操做。并且还化解了调用者和接收者之间的耦合关系。

特征

使用环境

在如下状况下可使用命令模式:

  • 系统须要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • 系统须要在不一样的时间指定请求、将请求排队和执行请求。
  • 系统须要支持命令的撤销(Undo)操做和恢复(Redo)操做。
  • 系统须要将一组操做组合在一块儿,即支持宏命令

优势&缺点

优势

命令模式的优势

  • 下降系统的耦合度。
  • 新的命令能够很容易地加入到系统中。
  • 能够比较容易地设计一个命令队列和宏命令(组合命令)。
  • 能够方便地实现对请求的Undo和Redo。
缺点

命令模式的缺点

  • 使用命令模式可能会致使某些系统有过多的具体命令类。由于针对每个命令都须要设计一个具体命令类,所以某些系统可能须要大量具体命令类,这将影响命令模式的使用。
相关文章
相关标签/搜索