Cowboy Tech

iOS设计模式-装饰

装饰模式

使用场景

  1. 比如引入一个静态库,只能导入.h文件。但不知道其具体实现,又想增添其功能
  2. 不想要过多的子类,不想通过继承来实现新方法

原理

  1. 装饰类,持有这个原始类的引用,原来对象的功能,装饰对象都有,让原始类引用调用自己的方法去实现。
  2. 装饰类为了扩展它的功能,通过装饰者实例化子类,实现更多的功能。

魂斗罗游戏案例

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 和装饰模式有细微的区别

  1. category给对象添加属性,可能会用刀objective-C runtime
  2. 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);

}