阅读 161

设计模式-命令模式

要解决的问题

示例1:到饭馆吃饭,你作为顾客,通常的情况是:服务员把菜单拿给你,点菜完成后,服务员就拿着菜单去向后厨交代。你只需要坐等菜好就可以了。你不需要知道谁给你做菜,也不需要知道他们怎么去做。只需要点菜就可以了。

示例2:平常我们开电脑的时候,只需要按下开机按钮就不用管了,电脑就开始启动、屏幕亮了起来。不需要关心电脑里主板、显卡、硬盘、操作系统等一些列的加载与检测等。

上述例子中点菜的顾客和开机的用户都是只做了下发命令的一个动作,其它都不需要管,就会有具体的行为或功能产生。将上述流程用编程的概念描述出来:客户端只是想要发出命令或者请求,不关心请求的真正接收者是谁,也不关心具体如何实现,而且同一个请求动作可以有不同的请求内容,当然具体的处理功能也不一样。这就是命令模式要解决的问题。


模式定义

命令模式(Command Pattern) 将一个请求封装为一个对象,从而使我们可以使用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,还被称为动作(Action)模式或事物(Transaction)模式。

命令模式的UML图如下:

  • Command:定义命令的接口(协议),声明执行的方法。
  • ConcreteCommand:具体的命令,实现Command接口,这里只是去调用真正的Receiver来实现真正要执行的功能或行为。
  • Receiver:命令接收者,命令行为的真正实现者。任何类都可能成为一个接收者,只要能够实现命令要求的功能。
  • Invoker:触发者,触发命令执行。通常会持有命令。
  • Client:创建具体的命令,并且把命令对象与接收者对象组装好。【这里并不是平常所说的客户端调用,客户端调用是从Invoker开始的】

具体实现

下边以电脑开机和重启两个命令来实现命令模式,UML图如下:

//***********************命令接口***********************
@protocol ComputerCommand <NSObject>
@property (nonatomic,copy)NSString *name;
- (void)execute;
@end
//***********************具体命令***************
@interface OpenCommand : NSObject<ComputerCommand>//开机命令
- (instancetype)initWithReceiver:(MainBoard *)mainBoard cmdName:(NSString *)cmdName;
@end

interface OpenCommand ()
@property (nonatomic,strong)MainBoard *mainBoard;
@end
@implementation OpenCommand
@synthesize name = _name;
- (instancetype)initWithReceiver:(MainBoard *)mainBoard cmdName:(NSString *)cmdName
{
    if (self = [super init]) {
        _mainBoard = mainBoard;
        _name = cmdName;
    }
    return self;
}
- (void)execute
{
    [self.mainBoard openComputer];
}
@end
//重启命令与开机命令代码类似,就不再往上贴了
//***********************命令接收者(真正的命令实现者)*********
@interface MainBoard : NSObject
- (void)openComputer;
- (void)closeComputer;
- (void)resetComputer;
@end

@implementation MainBoard
- (void)openComputer
{
    NSLog(@"接通电源、检查设备、装载系统、运行开机");
}
- (void)closeComputer
{
    NSLog(@"关机");
}
- (void)resetComputer
{
    NSLog(@"正在重启,请稍后,重启成功");
}
@end
//***********************命令触发者***********************
@interface ComputerBox : NSObject//Invoker  命令在主箱上
- (void)configOpenCommand:(id<ComputerCommand>)command;
- (void)configResetCommand:(id<ComputerCommand>)command;
- (void)pressOpenButton;
- (void)pressResetButton;
@end

@interface ComputerBox ()
@property (nonatomic,strong)id<ComputerCommand> openCommand;
@property (nonatomic,strong)id<ComputerCommand> resetCmd;
@end
@implementation ComputerBox
- (void)configOpenCommand:(id<ComputerCommand>)command
{
    _openCommand = command;
}
- (void)configResetCommand:(id<ComputerCommand>)command
{
    _resetCmd = command;
}
- (void)pressOpenButton
{
    [self.openCommand execute];
}
- (void)pressResetButton
{
    [self.resetCmd execute];
}
@end
//***********************将命令和接收者组装起来***************
@interface ComputerClient : NSObject
+ (id<ComputerCommand>)configedCommandWithCmdName:(NSString *)cmdName;
@end

@implementation ComputerClient
+ (id<ComputerCommand>)configedCommandWithCmdName:(NSString *)cmdName
{
    MainBoard *mainBoard = [MainBoard new];
    if ([cmdName isEqualToString:@"open"]) {
        OpenCommand *openCmd = [[OpenCommand alloc]initWithReceiver:mainBoard cmdName:cmdName];
        return openCmd;
    }else if ([cmdName isEqualToString:@"reset"]){
        ResetCommand *resetCmd = [[ResetCommand alloc]initWithReceiver:mainBoard cmdName:cmdName];
        return resetCmd;
    }
    return nil;
}
@end

//***********************外部调用***********************
//将命令和接收者组装
    id<ComputerCommand>  openCmd = [ComputerClient configedCommandWithCmdName:@"open"];
    id<ComputerCommand>  resetCmd = [ComputerClient configedCommandWithCmdName:@"reset"];
    //Invoker
    ComputerBox *box = [[ComputerBox alloc]init];
    
    //开机
    [box configOpenCommand:openCmd];//将命令给到触发者。可以将命令与接收者的组装也让Invoker实现。
    [box pressOpenButton];//点击开机按钮。触发命令
    
    //重启
    [box configResetCommand:resetCmd];
    [box pressResetButton];

复制代码

实现中的ComputerBox作为Invoker(触发者)角色,持有了开机命令和重启命令。ComputerClient类的作用就是将不同的命令和对应 的真正实现者Receiver装配起来,建立关系,该类的作用也可以由Invoker实现,不同的情况可以根据具体的需求自己来灵活变动实现。

总结

  • 命令模式的本质是封装请求:命令模式的关键是把请求封装称为命令对象,然后就可以对这个对象进行一系列处理。
  • 命令模式的优点:命令模式使得发起命令的对象--->客户端,和具体实现命令的对象--->接受者对象完全解耦。即发起命令的对象完全不知道具体实现对象是谁,也不知道如何实现。

命令模式还可以实现许多其他功能,例如:撤销操作、宏命令(一系列命令的组合)、队列请求等。在这里笔者只是简单的介绍了命令模式,对于命令模式的其它功能实现,后续会慢慢补充。

以上作为笔者自己的理解与记录,水平有限,如有理解错误的地方,还请指出。谢谢!

Demo地址


参考致谢:

《研磨设计模式》

《图说设计模式》

关注下面的标签,发现更多相似文章
评论