再讲具体内容之前我们先看一个《Head First 设计模式》里举的星巴克订单的例子,星巴克提供不同种类的咖啡和咖啡的调料,星巴克需要一些类来描述他们并且能计算出任意一种咖啡和任意几种调料搭配在一起的价格,如果我们用继承为每一种搭配写一个类的话,那就会有N多个类,如下图:
看到上面的类图瞬间真个人都不好了,是不是后悔选择了程序员这个行业,不要急,我们可以使用装饰者模式来让您的心情阳光起来。你也许会想到可以建了一个父类,把coffee的配料都写在里面,然后不同的coffee继承父类,这种方式比上面确实要好,不过带来很多数据冗余,因为父类的配料不是每种coffee都需要的。
使用装饰者模式后,上面的咖啡例子的UML图如下:
装饰者模式通过组合的方式扩展对象的特性,这种方式允许我们在任何时候对对象的功能进行扩展,甚至是运行时扩展,面向对象的设计有原则二是对扩展开放,对修改关闭,装饰者模式就很好的遵循了该原则。装饰者模式具有一些特征:
1.装饰者(decorator)和被装饰(扩展)的对象有着相同的超类(supertype)。
2.我们可以用多个装饰者去装饰一个对象。
3.我们可以用装饰过的对象替换代码中的原对象,而不会出问题(因为他们有相同的超类)。
4.装饰者可以在委托(delegate,即调用被装饰的类的成员完成一些工作)被装饰者的行为完成之前或之后加上他自己的行为。
5.一个对象能在任何时候被装饰,甚至是运行时。
上面来自https://www.cnblogs.com/coffeeSS/p/5405787.html,具体大家可以进去看。
装饰者的UML图如下:
Component:一般是抽象类,在iOS中可以使用类,也可以使用protocol实现,定义一些接口。
ConcreteComponetA:被修者A, iOS中继承Component类或者遵循Component协议
ConcreteComponetB:被修者B,同被修饰者A,可以有多个被修饰者
Decorator: iOS中继承Component类或者遵循Component协议,装饰者共同的父类
ConcreteDecoratorA:装饰者A,用来装饰ConcreteComponetA或者ConcreteComponetB
ConcreteDecoratorB:装饰者B,同装饰者A
下面我们来看一个吃火锅的例子,火锅有红锅和鸳鸯锅两种,每种火锅又可以添加不同的菜,最终的价格根据火锅的种类和菜的种类来计算价格。如果使用装饰者模式代码如下:
1.定义Component:使用协议来实现
@protocolHuoGuoProtocol
@optional
- (NSString*)getDesc;
- (double)getPrice;
@end
2.定义被被修饰者ConcreteComponetA红锅:
#import "HuoGuoProtocol.h"
@interface HongGuo : NSObject
- (instancetype)initWithType:(NSString*)type;
@end
@interface HongGuo()
@property (nonatomic, copy) NSString *type;
@end
@implementation HongGuo
- (instancetype)initWithType:(NSString*)type
{
self= [superinit];
if(self) {
_type= type;
}
return self;
}
- (NSString*)getDesc
{
return [NSString stringWithFormat:@"%@(%lf)",self.type,[self getPrice]];
}
- (double)getPrice
{
return 30.0;
}
@end
3.定义被被修饰者ConcreteComponetB鸳鸯锅:
#import "HuoGuoProtocol.h"
@interfaceYuanYangGuo :NSObject
- (instancetype)initWithType:(NSString*)type;
@end
#import "YuanYangGuo.h"
@interface YuanYangGuo()
@property (nonatomic, copy) NSString *type;
@end
@implementation YuanYangGuo
- (instancetype)initWithType:(NSString*)type
{
self= [superinit];
if(self) {
_type= type;
}
return self;
}
- (NSString*)getDesc
{
return [NSString stringWithFormat:@"%@(%lf)",self.type,[self getPrice]];
}
- (double)getPrice
{
return 50;
}
@end
4.定义Decorator:使用类来实现,遵循第一步的协议
#import "HuoGuoProtocol.h"
@interfaceHuoGuoDecorator :NSObject
@property (nonatomic, weak) id
- (instancetype)initWithHuoObj:(id)obj;
@end
#import "HuoGuoDecorator.h"
@implementationHuoGuoDecorator
- (instancetype)initWithHuoObj:(id)obj
{
self= [superinit];
if(self) {
_huoGuoObj= obj;
}
return self;
}
- (NSString*)getDesc
{
return [self.huoGuoObj getDesc];
}
- (double)getPrice
{
if (self.huoGuoObj)
{
return[self.huoGuoObjgetPrice];
}
return 0;
}
@end
5.定义装饰者ConcreteDecoratorA豆腐
@interface DouFu : HuoGuoDecorator
@end
#import "DouFu.h"
@implementation DouFu
- (NSString*)getDesc
{
return [NSString stringWithFormat:@"%@+%@(%lf)",[self.huoGuoObj getDesc],@"豆腐",5.0];
}
- (double)getPrice
{
return[self.huoGuoObjgetPrice] +5;
}
@end
6.同理定义装饰者ConcreteDecoratorB,ConcreteDecoratorC牛肉和土豆
7.调用
id
// id
HuoGuoDecorator*decorator = [[HuoGuoDecoratoralloc]initWithHuoObj:hongGuo];
DouFu*doufu = [[DouFualloc]init];
doufu.huoGuoObj= decorator;
Niurou*niuRou = [[Niuroualloc]init];
niuRou.huoGuoObj= doufu;
TuDou*tudou = [[TuDoualloc]init];
tudou.huoGuoObj= niuRou;
typeLabel.text= [tudougetDesc];
priceLabel.text= [NSStringstringWithFormat:@"price:%lf",[tudougetPrice]];
代码见:https://github.com/steven2008/DesignPattens.git