一、定义
装饰者模式动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰者模式采用了多用组合,少用继承
、对扩展开放,对修改关闭
的OO原则。一个组件可以被无数个装饰者包装。
二、使用场景
- 需要扩展一个类的功能,或给一个类添加附加职责。
- 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类。使得子类数目呈爆炸式增长。另一种可能是因为类定义被隐藏,或类定义不能用户生成子类。
三、具体实现
购买咖啡时,会根据要求加入各种调料,例如:牛奶、豆浆、摩卡等等,我们需要根据加入的不同调料收取不同的费用。下面我们使用装饰者模式来实现点单系统,返回正确的价格。
首先我们先确定咖啡是被装饰者,调料是装饰者,一个咖啡理论上可以添加无数的调料。
-
创建咖啡协议,所有的咖啡都有名称和价格属性。
// Baverage.h @protocol Baverage <NSObject> @required - (NSString *)name; - (double)cost; @end
-
创建调料协议,装饰者和被装饰者必须是一样的类型,也就是需要有共同的超类(咖啡和调料的共同超类是NSObject,分别实现不同的协议)
// Condiment.h @protocol Condiment <Baverage> - (instancetype)initWithBaverage:(id<Baverage>)baverage; @end
-
实现具体的咖啡对象,分别实现咖啡协议
// Espresso.m @implementation Espresso - (NSString *)name { return @"Espresso"; } - (double)cost { return 1.99; } @end // HouseBlend.m @implementation HouseBlend - (NSString *)name { return @"HouseBlend"; } - (double)cost { return 0.9; } @end
-
实现具体的调料对象,实现调料协议.需要引用一个咖啡实例。
// Moca.m @implementation Moca - (instancetype)initWithBaverage:(id<Baverage>)baverage { if (self = [super init]) { _baverage = baverage; } return self; } - (NSString *)name { return [NSString stringWithFormat:@"%@, Moca", [self.baverage name]]; } - (double)cost { return 0.3 + [self.baverage cost]; } @end // Milk.m @implementation Milk - (instancetype)initWithBaverage:(id<Baverage>)baverage { if (self = [super init]) { _baverage = baverage; } return self; } - (NSString *)name { return [NSString stringWithFormat:@"%@, Milk", [self.baverage name]]; } - (double)cost { return 0.1 + [self.baverage cost]; } @end
-
开始点咖啡
// 来一杯添加摩卡、牛奶、豆浆的浓缩咖啡 id<Baverage> baverage = [[Espresso alloc] init]; baverage = [[Moca alloc] initWithBaverage:baverage]; baverage = [[Milk alloc] initWithBaverage:baverage]; baverage = [[Soy alloc] initWithBaverage:baverage]; NSLog(@"%@ --- %lf",[baverage name], [baverage cost]); // 一杯加双倍摩卡的黑咖啡 id<Baverage> houseBlend = [[HouseBlend alloc] init]; houseBlend = [[Moca alloc] initWithBaverage:houseBlend]; houseBlend = [[Moca alloc] initWithBaverage:houseBlend]; NSLog(@"%@ --- %lf",[houseBlend name], [houseBlend cost]);
其实装饰模式在使用的时候,和程序中的递归操作很类似,逐级查询价格,逐级返回,最终得到一个总的价格。
四、总结
优点
- 装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者模式可以提供比继承更多的灵活性。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点
- 会在设计中加入大量的小类,如果过度使用,会让程序变得复杂。
- 增加代码的复杂度
- 需要相同的超类,否则会出现类型不匹配的问题。