iOS架构设计模式之命令模式

作者: meryin | 来源:发表于2018-07-31 16:43 被阅读50次

一 基本概念

Demo地址

  1. 定义:它属于行为型模式,将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。降低耦合度。
  2. 使用场景:应用程序支持撤销和恢复;记录请求日志,当系统故障时可以重新被执行;想用对象参数化一个动作以执行操作并且用不同命令来替换回调函数。
  3. 角色划分:
    接收者-> Reciever
    命令接口-> Command(CommandProtocol协议)
    具体命令-> ConcrateCommand
    请求者-> Invoker:持有命令对象的引用 然后执行对象的命令
    Client类:最终的客户端调用类。


二 基础案例

  1. CommandProtocol
//命令接口-协议
@protocol Meryin_CommandProtocol<NSObject>
//操作->具体实现方法
-(void)execute;
@end
  1. 接收者Reciever实现具体方法功能
//接收者
@interface Meryin_Reciever : NSObject
//具体方法
-(void)start;
@end

@implementation Meryin_Reciever
-(void)start{
    NSLog(@"接收者实现具体方法的功能");
}
@end
  1. ConcrateCommand 遵守协议,持有接收者,实现协议具体方法,调用具体逻辑
//具体命令-> ConcrateCommand
//遵守协议,实现协议具体方法
@interface Meryin_ConcrateCommand : NSObject<meryin_CommandProtocol>
- (instancetype)initWithReciever:(Meryin_Reciever *)reciever;
@end

@interface Meryin_ConcrateCommand()
@property(nonatomic, strong) Meryin_Reciever* reciever;
@end
@implementation Meryin_ConcrateCommand
- (instancetype)initWithReciever:(Meryin_Reciever *)reciever
{
    self = [super init];
    if (self) {
        self.reciever = reciever;
    }
    return self;
}

-(void)execute{
    [self.reciever start];
}
@end
  1. Invoker 请求者,持有ConcrateCommand的引用,执行ConcrateCommand遵守协议的方法;保存操作记录支持回退撤销。

@interface Meryin_Invoker()
@property (nonatomic,strong)NSMutableArray  *commands;
@property (nonatomic,strong)Meryin_Reciever  *reciever;
@property(nonatomic, strong)id <Meryin_CommandProtocol> command;
@end

@implementation Meryin_Invoker

- (instancetype)initWith:(Meryin_ConcrateCommand *)command reciever:(Meryin_Reciever *)reciver
{
    self = [super init];
    if (self) {
        self.command = command;
        self.reciever = reciver;
        self.commands = [NSMutableArray array];
    }
    return self;
}

-(void)toStart{
    [self.command execute];
    [self.commands addObject:[[Meryin_ConcrateCommand alloc]initWithReciever:self.reciever] ];
}

//支持撤销
//撤销最后一个命令
- (void)undoLastOne{
    if (self.commands.count >0) {
        NSLog(@"撤销--%lu",self.commands.count-1);
        //撤销
        [[self.commands lastObject] execute];
        //移除
        [self.commands removeLastObject];
    }
}
  1. Client 最后调用
 //创建接收者
        Meryin_Reciever  *reciver = [[Meryin_Reciever alloc]init];
        //创建命令(分离)->解藕和
        Meryin_ConcrateCommand *command = [[Meryin_ConcrateCommand alloc]initWithReciever:reciver];
        //创建请求者
        Meryin_Invoker  *invoker = [[Meryin_Invoker alloc]initWith:command reciever:reciver];
        [invoker toStart];
        [invoker toStart];
        [invoker undoLastOne];

三 基础命令模式的一步步优化

1. 动态命令

以上创建的命令模式,当命令过多时,类就会太多,不好管理,如果使用动态调用,用Block回调,不需要新建各种命令类,用Block实现一个命令类里面可以动态创建多个命令。

  • 创建动态命令类DynamicCommand,持有block,可以动态创建命令实现接收者的各种方法。
typedef void(^dynamicBlcok)(Meryin_Reciever *);
//解决方法:用block实现
@interface DynamicCommand : NSObject<meryin_CommandProtocol>
- (instancetype)initWith:(Meryin_Reciever *)reciver block:(dynamicBlcok)block;
//创建命令
+(id<meryin_CommandProtocol>)createCommand:(Meryin_Reciever *)reciver block:(dynamicBlcok)block;
@end

@interface DynamicCommand()
@property(nonatomic, strong) Meryin_Reciever* reciever;
@property(nonatomic, copy) dynamicBlcok  block;
@end

@implementation DynamicCommand
- (instancetype)initWith:(Meryin_Reciever *)reciver block:(dynamicBlcok)block
{
    self = [super init];
    if (self) {
        self.reciever = reciver;
        self.block = block;
    }
    return self;
}
- (void)execute{
    if (self.block) {
        self.block(self.reciever);
    }
}
//创建命令
+(id<meryin_CommandProtocol>)createCommand:(Meryin_Reciever *)reciver block:(dynamicBlcok)block{
    return [[DynamicCommand alloc]initWith:reciver block:block];
}
  • 动态命令的请求者,动态添加命令,实现接收者方法的回调
@interface DynamicCommandManager()
@property (nonatomic,strong)NSMutableArray  *commands;
@property (nonatomic,strong)Meryin_Reciever  *reciever;
@end

@implementation DynamicCommandManager

- (instancetype)initWith:(Meryin_Reciever *)reciver{
    self = [super init];
    if (self) {
        self.reciever = reciver;
        self.commands = [NSMutableArray array];
    }
    return self;
}
-(void)toStart{
    [self addCommand:@"start"];
    [ self.reciever start];
}
- (void)toEnd{
    [self addCommand:@"end"];
    [self.reciever end];
}
- (void)addCommand:(NSString *)methodName{
    //动态添加命令
   DynamicCommand *command =[DynamicCommand createCommand:self.reciever block:^(Meryin_Reciever *block) {
   SEL method = NSSelectorFromString(methodName);
        //执行回调
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [block performSelector:method];
#pragma clang diagnostic pop
    }];
    [self.commands addObject:command];
}
//撤销最后一个命令
- (void)undoLastOne{
    if (self.commands.count >0) {
        NSLog(@"撤销--%lu",self.commands.count-1);
        //撤销
        [[self.commands lastObject] execute];
        //移除
        [self.commands removeLastObject];
    }
}
//撤销所有
- (void)undoAll{
    if (self.commands.count >0) {
        NSLog(@"撤销所有");
        //撤销
        for (id <meryin_CommandProtocol> command in self.commands) {
            [command execute];
        }
        //移除
        [self.commands removeAllObjects];
    }
}
  • Client客户端调用
//动态命令
        Meryin_Reciever  *reciver1 = [[Meryin_Reciever alloc]init];
        DynamicCommandManager  *manager1 = [[DynamicCommandManager alloc]initWith:reciver1];
        [manager1 toEnd];
        [manager1 toStart];
        [manager1 toStart];
        [manager1 toEnd];
        [manager1 undoByIndex:2];

命令模式并不去实现具体业务逻辑,只用于记录撤销等。具体撤销逻辑由Client决定,命令模式只是保存操作。

2. 复合命令

执行多个命令叫复合命令

  • 新建复合命令WrapperCommand,遵守协议
@interface WrapperCommand : NSObject<meryin_CommandProtocol>
- (instancetype)initWith:(NSMutableArray *)commands;
@end

@interface WrapperCommand()
@property(nonatomic, strong) NSMutableArray* commands;
@end
@implementation WrapperCommand
- (instancetype)initWith:(NSMutableArray *)commands
{
    self = [super init];
    if (self) {
        self.commands = commands;
    }
    return self;
}
- (void)execute{
    for (id<meryin_CommandProtocol> item in self.commands) {
        [item execute];
    }
}
  • 请求者-复合命令管理器WrapperCommandManager
    跟动态命令DynamicCommandManager类似,只有撤销所有的方法不一样
//撤销所有
- (void)undoAll{
    if (self.commands.count >0) {
        NSLog(@"撤销所有");
        //撤销
        for (id <meryin_CommandProtocol> command in self.commands) {
            [command execute];
        }
        //移除
        [self.commands removeAllObjects];
    }
}
3. 泛型命令

在定义的时候不需要指定类型,在使用的时候指定类型。c++里叫模版。

  • 新建GenericsCommand泛型命令,接收者是动态的,可以持有任何接收者
//可以接受所有接收者
//T任意类型的标记
@interface GenericsCommand<T> : NSObject<meryin_CommandProtocol>
- (instancetype)initWith:(T)reciver block:(void(^)(T))commandBlock;
+(id<meryin_CommandProtocol>)createCommand:(T)reciver block:(void (^)(T))block;
@end

@interface GenericsCommand<T>()
@property(nonatomic, strong) T reciever;
@property(nonatomic, copy) void(^block)(T);
@end
@implementation GenericsCommand

//id 指向泛型类型的引用
- (instancetype)initWith:(id)reciver block:(void(^)(id))commandBlock
{
    self = [super init];
    if (self) {
        self.reciever = reciver;
        self.block = commandBlock;
    }
    return self;
}
//创建命令
+(id<meryin_CommandProtocol>)createCommand:(id)reciver block:(void (^)(id))block{
    return [[GenericsCommand alloc]initWith:reciver block:block];
}
- (void)execute{
    if (self.block) {
        self.block(self.reciever);
    }
}
  • 泛型命令管理器,用泛型命令创建命令集合
@implementation GenericsCommandManager
- (instancetype)initWith:(Meryin_Reciever *)reciver{
    self = [super init];
    if (self) {
        self.reciever = reciver;
        self.commands = [NSMutableArray array];
    }
    return self;
}
-(void)toStart{
    [self addCommand:@"start"];
    [ self.reciever start];
}
- (void)toEnd{
    [self addCommand:@"end"];
    [self.reciever end];
}
- (void)addCommand:(NSString *)methodName{
    //动态添加命令
   GenericsCommand *command =[GenericsCommand createCommand:self.reciever block:^(Meryin_Reciever *block) {
    SEL method = NSSelectorFromString(methodName);
        //执行回调
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [block performSelector:method];
#pragma clang diagnostic pop
    }];
    [self.commands addObject:command];
}
//撤销所有
- (void)undoAll{
    if (self.commands.count >0) {
        NSLog(@"撤销所有");
        //撤销
        WrapperCommand *commands = [[WrapperCommand alloc]initWith:self.commands];
        [commands execute];
        //移除
        [self.commands removeAllObjects];
    }
}
4. 并发处理
- (void)addCommand:(NSString *)methodName{
    dispatch_sync(self.queue, ^{
        //动态添加命令
        GenericsCommand *commnad = [GenericsCommand createCommand:self.reciever block:^(Meryin_Reciever *block) {
        SEL method = NSSelectorFromString(methodName);
            //执行回调
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [block performSelector:method];
#pragma clang diagnostic pop
        }];
        [self.commands addObject:commnad];
    });
}
5. 命令接口 Command用block

之前CommandProtocol用的是协议,现在用block,直接用管理器,队列里保存的是block

- (void)addCommand:(NSString *)methodName{
    dispatch_sync(self.queue, ^{
        SEL method = NSSelectorFromString(methodName);
        //动态添加命令
        blcokCommand block = ^(Meryin_Reciever *reciver){
            //执行回调
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [reciver performSelector:method];
#pragma clang diagnostic pop
        };
        [self.commands addObject:block];
       
    });
}
//撤销第几个 从0开始
- (void)undoByIndex:(NSInteger )index{
    if (self.commands.count >0 && self.commands.count > index) {
        NSLog(@"撤销--%lu",index);
        //撤销
        blcokCommand commandBlock= [self.commands objectAtIndex:index] ;
         commandBlock(self.reciever);
        //移除
        [self.commands removeObjectAtIndex:index];
    }
}

四 命令模式应用场景-购物车商品的添加删减

命令模式的撤销和其他业务逻辑分开,商品的增加和删减可以和其他业务逻辑分开,可以减少controller的代码量
分析角色:命令接口CommandProtocol,具体命令GenericsCommand,接收者ShoppingRecevier,请求者GenericsCommandManager
实际案例中命令保存时成对出现,比如减少购物车商品的撤销其实就是增加购物车商品

- (void)addGood:(NSString *)value withMode:(GoodsMode*)goods{
    [self addCommand:@"reduceGood:withMode:" value:value model:goods];
     [self.reciever addGood:value withMode:goods];
}
- (void)reduceGood:(NSString *)value withMode:(GoodsMode*)goods{
    [self addCommand:@"addGood:withMode:" value:value model:goods];
    [self.reciever reduceGood:value withMode:goods];
}
//保存命令
- (void)addCommand:(NSString *)methodName value:(NSString *)value model:(GoodsMode*)goods{
  
    //动态添加命令
   [self.commands addObject: [GenericsCommand createCommand:self.reciever block:^(ShoppingRecevier *reciver) {
          SEL method = NSSelectorFromString(methodName);
        //执行回调
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        if (reciver && method){
            [reciver performSelector:method withObject:value withObject:goods];
        }
#pragma clang diagnostic pop
   }]];
}

相关文章

网友评论

本文标题:iOS架构设计模式之命令模式

本文链接:https://www.haomeiwen.com/subject/qtetpftx.html