装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

 

比如游戏机有一个GamePad类, 现在要增加一个作弊功能(例如100条命), 如果直接在GamePad类中去添加可能会影响其他子类的使用

我们考虑装饰模式思维, 先建立一个装饰器实现GamePad的所有功能, 然后在装饰器类的子类中去添加作弊放方法

上代码

比如GamePad类是这样

复制代码
 1 #import <Foundation/Foundation.h>  2  3 @interface GamePad : NSObject  4  5 - (void)up;  6 - (void)down;  7 - (void)left;  8 - (void)right;  9 - (void)buttonA; 10 - (void)buttonB; 11 12 @end
复制代码

 

我们创建一个装饰器类, 让它持有一个GamePad实例并实现相同的方法接口

GamePadDecorator.h

复制代码
 1 #import <Foundation/Foundation.h>  2 #import "GamePad.h"  3  4 @interface GamePadDecorator : NSObject  5  6 - (void)up;  7 - (void)down;  8 - (void)left;  9 - (void)right; 10 - (void)buttonA; 11 - (void)buttonB; 12 13 @end
复制代码

GamePadDecorator.m

复制代码
 1 #import "GamePadDecorator.h"  2  3 @interface GamePadDecorator ()  4  5 @property (nonatomic, strong) GamePad *gamePad;  6  7 @end  8  9 @implementation GamePadDecorator 10 11 - (instancetype)init { 12 self = [super init]; 13 if (self) { 14 self.gamePad = [[GamePad alloc] init]; 15  } 16 return self; 17 } 18 19 - (void)up { 20  [self.gamePad up]; 21 } 22 23 - (void)down { 24  [self.gamePad down]; 25 } 26 27 - (void)left { 28  [self.gamePad left]; 29 } 30 31 - (void)right { 32  [self.gamePad right]; 33 } 34 35 - (void)buttonA { 36  [self.gamePad buttonA]; 37 } 38 39 - (void)buttonB { 40  [self.gamePad buttonB]; 41 } 42 43 @end
复制代码

现在我们新增一个子类来实现作弊方法

CheatGamePadDecorator.h

复制代码
1 #import "GamePadDecorator.h" 2 3 @interface CheatGamePadDecorator : GamePadDecorator 4 5 - (void)cheat; 6 7 @end
复制代码

CheatGamePadDecorator.m

复制代码
1 #import "CheatGamePadDecorator.h" 2 3 @implementation CheatGamePadDecorator 4 5 - (void)cheat { 6 NSLog(@"cheat"); 7 } 8 9 @end
复制代码

 

这样我们就可以直接在Controller中直接用CheatGamePadDecorator类去实现GamePad的所有功能还能额外实现作弊方法

复制代码
 1 #import "ViewController.h"  2 #import "CheatGamePadDecorator.h"  3  4 @interface ViewController ()  5  6 @end  7  8 @implementation ViewController  9 10 - (void)viewDidLoad { 11  [super viewDidLoad]; 12 13 //创建CheatGamePadDecorator实例 14 CheatGamePadDecorator *cheaterGamePad = [[CheatGamePadDecorator alloc] init]; 15 16 //实现GamePad的功能 17  [cheaterGamePad up]; 18  [cheaterGamePad down]; 19 20 //实现作弊方法 21  [cheaterGamePad cheat]; 22 } 23 24 25 26 @end
复制代码

这样就完成了一个装饰模式思路的代码构建

 

下面说说cocoa touch中自带的Category, 它也是对装饰模式的一个实现

我们用Category来实现上面GamePad添加作弊功能

我们创建一个Cheat Category

GamePad+Cheat.h

复制代码
1 #import "GamePad.h" 2 3 @interface GamePad (Cheat) 4 5 - (void)cheat; 6 7 @end
复制代码

GamePad+Cheat.m

复制代码
1 #import "GamePad+Cheat.h" 2 3 @implementation GamePad (Cheat) 4 5 - (void)cheat { 6 NSLog(@"cheat"); 7 } 8 9 @end
复制代码

 

这样我们就可以直接在Controller中通过Category来实现上面功能

复制代码
 1 #import "ViewController.h"  2 #import "GamePad+Cheat.h"  3  4 @interface ViewController ()  5  6 @end  7  8 @implementation ViewController  9 10 - (void)viewDidLoad { 11  [super viewDidLoad]; 12 13 //创建GamePad实例 14 GamePad *gamePad = [[GamePad alloc] init]; 15 16 //实现GamePad原有方法 17  [gamePad up]; 18  [gamePad down]; 19 20 //实现作弊方法 21  [gamePad cheat]; 22 23 }
复制代码

使用Category更为简单

但是在使用Category时有个细节一定要注意, 尽量不要在Category类中去重写基类方法

假如我们在GamePad+Cheat.h中重写了- (void)up方法, 则整个工程中的up方法都被重载了

即使我们不在任何地方引用GamePad+Cheat.h, 只要这个文件在工程里面就会让GamePad方法被重载