iOS设计模式的应用 ⑱ 命令模式

前言

    在战场上,将军把封在信封里的定时指令交给不下,然后他们到时候打开信封并执行其中的命令。同样的指令既可以是一次性的,也可以是能被不同人在指定时间再次执行的。因为指令封在信封里,所以为了各种目的在不同地区之间传递起来会比其它方式更为容易。

    在面向对象设计中,我们借用了类似的思想,把指令封装在各种命令对象中。命令对象可以被传递并且在指定时刻被不同的客户端复用。从这一概念设计而来的设计模式叫做命令模式

什么是命令模式

    命令设计模式将请求封装为对象,从而可以使用不同的请求对客户进行参数化,对请求队列或记录请求日志,以及支持可撤消的操作。命令对象封装了如何对目标执行指令的信息。因此客户端或调用者不必了解目标的任何细节,却仍可以对它执行任何已有的操作。命令对象将一个或多个动作绑定到特定的接收器,命令模式消除了作为对象的动作和执行它的接收器之间的绑定。

命令模式结构的类图.png
  • Client创建 ConcreteCommand 对象并设定其 Receiver
  • Invoker 要求通用命令实施请求
  • CommandInvoker 所知的协议
  • ConcreteCommand 起到 Receiveraction之间的中间人的作用
  • Receiver 可以是随着由 ConcreteCommand 对象实施的相应请求,而执行实际操作的任何对象。

什么时候使用命令模式

  • 想让应用程序支持撤销与恢复
  • 想用对象参数化一个动作以执行操作,并用不同命令对象来代替回调函数
  • 想要在不同时刻对请求进行指定、排列和执行。
  • 想要记录修改日志,这样在系统故障时,这些修改可以在后来重做一遍。
  • 想让系统支持事务,事务封装了对数据的一系列修改。事务可以建模为命令对象。

命令模式的优缺点

命令模式的优点

  1. 降低了系统耦合度。
  2. 新的命令可以很容易添加到系统中去。

命令模式的缺点

使用命令模式可能会导致某些系统有过多的具体命令类。

Cocoa中的命令模式

NSInvocation

    NSInvocation 类的实例封装了一个Objective-C 消息。调用对象包含目标对象(target)、方法选择器(selector)和方法参数(parameters)。可以借助于 NSInvocation 动态更改调用对象发送的消息的目标及其参数,还可以从对象中获取返回值。使用同一个 NSInvocation 实例可以重复调用接收器的同一个方法,或者通过不同的目标和方法签名进行复用。

   NSInvocation 对象的创建需要 NSMethodSignature 对象。 NSMethodSignature 对象封装了方法的参数和返回值相关的类型信息。NSMethodSignature对象是通过方法选择器创建的。NSInvocation的实现还利用了 Objective-C 运行时的功能。

Target - Action

   Target - Action 机制使控制对象(即按钮、滑块或文本字段等对象)能够将消息发送到另一个对象,该对象可以解释消息并将其作为特定于应用程序的指令进行处理。接收对象(target),通常是一个自定义的控制器对象。消息——命名为动作消息——由选择器决定,一个方法的唯一运行时标识符。

NSUndoManager

     NSUndoManager作为通用的撤销栈的管理类,其撤销栈以对象形式保持所有已调用的操作。通过调用从撤销栈压入恢复栈的操作对象。NSUndoManager 在两个栈之间移动操作对象,管理着整个命令历史记录。

命令模式的实现

首先创建作为命令的接口 <Order>

@protocol Order <NSObject>

- (void)execute;

@end

创建作为请求的 Stock

@interface Stock : NSObject

@property (nonatomic, copy) NSString *name;


@property (nonatomic, assign) int quantity;

- (void)buy;
- (void)sell;

@end
@implementation Stock



- (void)buy{
    NSLog(@"Buy");
}

- (void)sell{
    NSLog(@"Sell");
}
@end

创建实体命令类 BuyStockSellStock,实现 <Order> 接口,将执行实际的命令处理

@interface BuyStock : NSObject <Order>

- (instancetype)initWithStock:(Stock *)abcStock;

@end
@implementation BuyStock
{
    Stock *_aStock;
}

- (instancetype)initWithStock:(Stock *)aStock;
{
    self = [super init];
    if (self) {
        _aStock  = aStock;
    }
    return self;
}

- (void)execute{
    [_aStock buy];
}

@end
@interface SellStock : NSObject <Order>

- (instancetype)initWithStock:(Stock *)abcStock;

@end
@implementation SellStock
{
    Stock *_aStock;
}

- (instancetype)initWithStock:(Stock *)aStock;
{
    self = [super init];
    if (self) {
        _aStock  = aStock;
    }
    return self;
}

- (void)execute{
    [_aStock sell];
}

@end

创建作为调用对象的类 Broker,它接受订单并能下订单。Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。

@interface Broker : NSObject

- (void)takeOrder:(id <Order>)order;

- (void)placeOrders;

@end
@implementation Broker{
    NSMutableArray <id<Order>>* _orderList;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _orderList = [NSMutableArray array];
    }
    return self;
}

- (void)takeOrder:(id <Order>)order{
    [_orderList addObject:order];
}

- (void)placeOrders{
    for (id <Order> order in _orderList) {
        [order execute];
    }
    [_orderList removeAllObjects];
}

@end

最后使用 Broker 类来演示命令模式。

Stock *aStock = [[Stock alloc] init];
aStock.name = @"ABC";
aStock.quantity = 10;
BuyStock *buyStockOrder = [[BuyStock alloc] initWithStock:aStock];
SellStock *sellStockOrder = [[SellStock alloc] initWithStock:aStock];

Broker *broker = [[Broker alloc] init];
[broker takeOrder:buyStockOrder];
[broker takeOrder:sellStockOrder];
[broker placeOrders];

总结

    在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。命令模式通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。命令模式可以在单击试图控制器中的按钮时,执行一个命令对象,对另一个视图控制器进行某些操作,也可以实现撤销和恢复时使用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 算法封装,通过封装和拓展对象的算法来改变对象的行为。涉及到算法封装的有以下三种设计模式:① 模板方法(封装共用行为...
    WellsCai阅读 2,197评论 0 1
  • 一、概念 1、命令模式的动机 ​ 夏天撸串加冰啤酒简直爽翻天,我们可以通过向服务员点菜烤羊肉、烤鱼、冰啤酒等,然...
    阿饼six阅读 1,312评论 0 4
  • 什么是命令模式 命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客...
    退役程序员Franco阅读 1,114评论 0 3
  • 一、当我们谈论设计模式的时候,我们在谈什么? 设计模式是为特定场景下的问题而定制的解决方案,是对特定面向对象设计问...
    苦艾酒艾阅读 346评论 0 0
  • 定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销...
    helinyu阅读 154评论 0 1