命令模式是将请求封装成对象。
屏幕快照 2017-12-17 下午10.33.22.png
你要做一个控制家庭智能电器的app。每一个电器的控制都要其自己的api。
很明显,要使用的主UI是UITableView,而对于每个接口的调用,肯定不会去写一坨if else。正面登场的就是命令模式。
1 先介绍一下厂商提供的智能电器接口:
1.1 电灯
#import <Foundation/Foundation.h>
@interface Light : NSObject
@property (nonatomic, strong) NSString *name;
// 初始化电灯并设置电灯的名称
- (instancetype)initWithName:(NSString *)name;
// 打开电灯
- (void)on;
// 关闭电灯
- (void)off;
@end
1.2 音响
#import <Foundation/Foundation.h>
@interface Stereo : NSObject
@property (nonatomic, strong) NSString *stereoName;
// 初始化音响并设置音响的名称
- (instancetype)initWithName:(NSString *)name;
// 打开音响
- (void)stereoOn;
// 关闭音响
- (void)stereoOff;
@end
这两个api来自己同一个厂商,所以接口相似
2 开始命令模式
2.1 首先,设计一个Command的接口
#import <Foundation/Foundation.h>
@protocol Command <NSObject>
// 执行命令
- (void)execute;
@end
2.2 然后,设计一个BaseCommand的基类,并且遵守上述协议接口
#import <Foundation/Foundation.h>
#import "Command.h"
@interface BaseCommand : NSObject<Command>
@end
对于协议中的接口,基类并不做具体的实现。
2.3 设计一个控制开灯的命令
一个继承BaseCommand的类,其初始化方法中需要接收厂商提供的Light对象,并在execute方法的实现中调用ligth对象的on接口
#import "BaseCommand.h"
@class Light;
@interface LightOnCommand : BaseCommand
// 初始化开灯命令
- (instancetype)initWith:(Light *)light;
@end
其内部实现
#import "LightOnCommand.h"
#import "Light.h"
@interface LightOnCommand ()
@property (nonatomic, strong) Light *light;
@end
@implementation LightOnCommand
- (instancetype)initWith:(Light *)light{
self = [super init];
if (self) {
self.light = light;
}
return self;
}
- (void)execute{
[super execute];
[self.light on];
}
@end
2.4 设计一个控制关灯的命令
与上述命令实现类似,只是其execute命令的实现为调用灯的off接口
2.5设计一个控制音响打/开关闭的接口
同上
3 功能实现
3.1 自定义UITableViewCell
#import "Command.h"
@interface ControlCell : UITableViewCell
// 传递开/关命令 及电器名称
- (void)setOnCommand:(id<Command>)onCommand offCommand:(id<Command>)offCommand slotName:(NSString *)name;
@end
在.m实现里,分别 用变量保存上述开/关 命令,并在触发cell中的Button点击事件时,调用相应命令的execute方法
- (void)setOnCommand:(id<Command>)onCommand offCommand:(id<Command>)offCommand slotName:(NSString *)name{
_onCommand = onCommand;
_offCommand = offCommand;
_slot.text = name;
}
- (IBAction)appliancesOn:(id)sender {
[_onCommand execute];
}
- (IBAction)appliancesOff:(id)sender {
[_offCommand execute];
}
3.2数据源的创建
分别用三个数组来存放一一对应的电器的开命令、关命令及名称
// 开命令
@property (nonatomic, strong) NSMutableArray<id<Command>> *onCommands;
// 关命令
@property (nonatomic, strong) NSMutableArray<id<Command>> *offCommands;
// 电器名称
@property (nonatomic, strong) NSMutableArray<NSString *> *slotNames;
onCommands及slotNames数组的初始化
-(NSMutableArray *)onCommands{
if (_onCommands == nil) {
_onCommands = [NSMutableArray new];
_slotNames = [NSMutableArray new];
LightOnCommand *ligthOn = [[LightOnCommand alloc] initWith:_livingRoomLight];
[_onCommands addObject:ligthOn];
[_slotNames addObject:_livingRoomLight.name];
ligthOn = [[LightOnCommand alloc] initWith:_kitchenLight];
[_onCommands addObject:ligthOn];
[_slotNames addObject:_kitchenLight.name];
StereoOnWithCDCommand *stereoOn = [[StereoOnWithCDCommand alloc] initWithStereo:_livingRoomStereo];
[_onCommands addObject:stereoOn];
[_slotNames addObject:_livingRoomStereo.stereoName];
NoCommand *noCommand = [NoCommand new];
for (int i = 0; i < 5; ++i) {
[_onCommands addObject:noCommand];
[_slotNames addObject:@"NULL"];
}
}
return _onCommands;
}
offCommands数组与上述相似
3.3 给tablewView赋值
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
ControlCell *cell = [tableView dequeueReusableCellWithIdentifier:CELLID forIndexPath:indexPath];
NSUInteger slot = indexPath.row;
BaseCommand *onCo = _onCommands[slot];
BaseCommand *offCo = _offCommands[slot];
[cell setOnCommand:onCo offCommand:offCo slotName:_slotNames[slot]];
onCo.lastComDelegate = self;
offCo.lastComDelegate = self;
return cell;
}
完美实现
demo地址
码云














网友评论