1. 程式人生 > IOS開發 >iOS設計模式之(六)命令模式

iOS設計模式之(六)命令模式

前言

什麼是命令模式

命令模式 屬於行為型模式,在百度百科中的定義如下

在軟體系統中,“行為請求者”與“行為實現者”通常呈現一種“緊耦合”。但在某些場合,比如要對行為進行“記錄、撤銷/重做、事務”等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將“行為請求者”與“行為實現者”解耦?將一組行為抽象為物件,實現二者之間的鬆耦合。這就是命令模式(Command Pattern)

模式結構

Command:

定義命令的介面,宣告執行的方法。

ConcreteCommand:

命令介面實現物件,是“虛”的實現;通常會持有接收者,並呼叫接收者的功能來完成命令要執行的操作。

Receiver:

接收者,真正執行命令的物件。任何類都可能成為一個接收者,只要它能夠實現命令要求實現的相應功能。

Invoker:

要求命令物件執行請求,通常會持有命令物件,可以持有很多的命令物件。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,也就是說相當於使用命令物件的入口。

Client:

建立具體的命令物件,並且設定命令物件的接收者。注意這個不是我們常規意義上的客戶端,而是在組裝命令物件和接收者,或許,把這個Client稱為裝配者會更好理解,因為真正使用命令的客戶端是從Invoker來觸發執行。

模式分析

  • 命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開。
  • 每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,並執行操作。
  • 命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的介面,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。
  • 命令模式使請求本身成為一個物件,這個物件和其他物件一樣可以被儲存和傳遞。
  • 命令模式的關鍵在於引入了抽象命令介面,且傳送者針對抽象命令介面程式設計,只有實現了抽象命令介面的具體命令才能與接收者相關聯。

程式碼

場景

在當前控制器中心建立一個紅色view,增加三個按鈕,分別是放大,縮小,回退。對應的紅色view就會放大,縮小,回退。

ViewController

建立ViewController類,顯示redView,並有三個按鈕

#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

負責具體實現的細節

#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,_width);
}

@end


複製程式碼

介面命令CommandProtocol

#import <Foundation/Foundation.h>


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

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

@end


複製程式碼

具體的命令

amplifyCommand 放大

#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 縮小

#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,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

#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地址