iOS設計模式之(六)命令模式
阿新 • • 發佈:2019-12-25
前言
什麼是命令模式
在軟體系統中,“行為請求者”與“行為實現者”通常呈現一種“緊耦合”。但在某些場合,比如要對行為進行“記錄、撤銷/重做、事務”等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將“行為請求者”與“行為實現者”解耦?將一組行為抽象為物件,實現二者之間的鬆耦合。這就是命令模式(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
複製程式碼
模式優點
- 降低物件之間的耦合度。
- 新的命令可以很容易地加入到系統中。
- 可以比較容易地設計一個組合命令。
- 呼叫同一方法實現不同的功能
模式缺點
- 使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。