設計模式-命令模式

要解決的問題

示例1:到飯館吃飯,你做爲顧客,一般的狀況是:服務員把菜單拿給你,點菜完成後,服務員就拿着菜單去向後廚交代。你只須要坐等菜好就能夠了。你不須要知道誰給你作菜,也不須要知道他們怎麼去作。只須要點菜就能夠了。html

示例2:日常咱們開電腦的時候,只須要按下開機按鈕就不用管了,電腦就開始啓動、屏幕亮了起來。不須要關心電腦裏主板、顯卡、硬盤、操做系統等一些列的加載與檢測等。git

上述例子中點菜的顧客和開機的用戶都是隻作了下發命令的一個動做,其它都不須要管,就會有具體的行爲或功能產生。將上述流程用編程的概念描述出來:客戶端只是想要發出命令或者請求,不關心請求的真正接收者是誰,也不關心具體如何實現,並且同一個請求動做能夠有不一樣的請求內容,固然具體的處理功能也不同。這就是命令模式要解決的問題。github


模式定義

命令模式(Command Pattern) 將一個請求封裝爲一個對象,從而使咱們能夠使用不一樣的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。命令模式是一種對象行爲型模式,還被稱爲動做(Action)模式或事物(Transaction)模式。編程

命令模式的UML圖以下: 設計模式

  • Command:定義命令的接口(協議),聲明執行的方法。
  • ConcreteCommand:具體的命令,實現Command接口,這裏只是去調用真正的Receiver來實現真正要執行的功能或行爲。
  • Receiver:命令接收者,命令行爲的真正實現者。任何類均可能成爲一個接收者,只要可以實現命令要求的功能。
  • Invoker:觸發者,觸發命令執行。一般會持有命令。
  • Client:建立具體的命令,而且把命令對象與接收者對象組裝好。【這裏並非日常所說的客戶端調用,客戶端調用是從Invoker開始的】

具體實現

下邊以電腦開機和重啓兩個命令來實現命令模式,UML圖以下: bash

//***********************命令接口***********************
@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實現,不一樣的狀況能夠根據具體的需求本身來靈活變更實現。ui

總結

  • 命令模式的本質是封裝請求:命令模式的關鍵是把請求封裝稱爲命令對象,而後就能夠對這個對象進行一系列處理。
  • 命令模式的優勢:命令模式使得發起命令的對象--->客戶端,和具體實現命令的對象--->接受者對象徹底解耦。即發起命令的對象徹底不知道具體實現對象是誰,也不知道如何實現。

命令模式還能夠實現許多其餘功能,例如:撤銷操做、宏命令(一系列命令的組合)、隊列請求等。在這裏筆者只是簡單的介紹了命令模式,對於命令模式的其它功能實現,後續會慢慢補充。atom

以上做爲筆者本身的理解與記錄,水平有限,若有理解錯誤的地方,還請指出。謝謝!spa

Demo地址操作系統


參考致謝:

《研磨設計模式》

《圖說設計模式》

相關文章
相關標籤/搜索