装饰模式
使用场景
- 比如引入一个静态库,只能导入.h文件。但不知道其具体实现,又想增添其功能
- 不想要过多的子类,不想通过继承来实现新方法
原理
- 装饰类,持有这个原始类的引用,原来对象的功能,装饰对象都有,让原始类引用调用自己的方法去实现。
- 装饰类为了扩展它的功能,通过装饰者实例化子类,实现更多的功能。
魂斗罗游戏案例
GamePad
//在游戏开始时作弊,需要按下很多按钮,能否设置一个按钮来快捷实现
- (void)up {
NSLog(@"up");
}
- (void)down {
NSLog(@"down");
}
GamePadDecorator
//装饰基类,持有这个原始类的引用,让原始类引用调用自己的方法去实现
@property (nonatomic, strong) GamePad *gamePad;
- (instancetype)init {
self = [super init];
if (self) {
self.gamePad = [[GamePad alloc] init];
}
return self;
}
- (void)up {
[self.gamePad up];
}
- (void)down {
[self.gamePad down];
}
CheatGamePadDecorator
//实例化装饰基类,添加新的方法
@interface CheatGamePadDecorator : GamePadDecorator
- (void)cheat {
[self up];
[self down];
[self up];
[self down];
[self left];
[self right];
[self left];
[self right];
[self commandA];
[self commandB];
[self commandA];
[self commandB];
}
ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
GamePadDecorator *gamePadDecorator = [[GamePadDecorator alloc] init];
[gamePadDecorator up];
CheatGamePadDecorator *cheatPamePad = [[CheatGamePadDecorator alloc] init];
[cheatPamePad cheat];
}
框架级别的装饰:Category
category 和装饰模式有细微的区别
- category给对象添加属性,可能会用刀objective-C runtime
- category重写被装饰对象方法,如果不小心在category里重写了被装饰对象的方法,即使没有引用这个category,使用的时候,也会执行override后的新方法
千万不要重写原始类的方法,否则会改变原始类的行为
GamePad(Cheat)
@implementation GamePad (Cheat)
- (void)cheat {
[self up];
[self down];
......
}
GamePad (Coin)
//需要用到objective-C runtime
@property (nonatomic) NSInteger coin;
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
@implementation GamePad (Coin)
static const NSString *_coinStr = @"_coinStr";
- (void)setCoin:(NSInteger)coin {
objc_setAssociatedObject(self, (__bridge const void *)_coinStr, @(coin), OBJC_ASSOCIATION_ASSIGN);
}
- (NSInteger)coin {
NSNumber *number = objc_getAssociatedObject(self, (__bridge const void *)_coinStr);
return number.integerValue;
}
ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GamePad *gamePad = [[GamePad alloc] init];
[gamePad up];
//被装饰后的新属性
gamePad.coin = 10;
NSLog(@"coin %ld", (long)gamePad.coin);
}