装饰器模式(decorator)给一个对象动态地添加职责,而无需为了每一种可能的职责配置情况去创建特定的子类(派生类) 《代码大全》 第二版p102
当需要为一个类增加新功能,我们第一想法通常是通过继承。设想一个这样的例子,你在设计装修一个房间,你已经实现了许多的功能(类),比如这个类会为房间开个窗户,那个类会为房间挂一张画,这些类组合起来完成的功能就是最终房间的样子。
class getAWindow{
void getWindow(){
put a window on the wall.
}
}
class getAPainting{
void getPainting(){
hang a painting in the room.
}
}
class otherFunction .......
那么要实现一个新的房间,你需要组合以上这些功能,如果用多重继承,那么需要给每一种组合都创造一个派生类,还得注意有没有函数重复定义。
class allFunc:publc getAWindow, getAPainting ... {
//调用所有的函数
do(){
getWindow();
getPainting();
}
}
如果需求有一点改动,比如你要把墙刷成黄色,你创造的新类(你总不希望设计了那么久只用在一个房间里吧)当然可以直接从allFunc继承
class yellowWall:public allFunc{
do2(){
allFunc::do();
brushyellow();
}
}
到这里貌似还没有出现什么问题,只要每次增加一个功能建立一个派生类就行了,但是如果出现了这样一个需求,用户很喜欢你的整套设计,唯独不喜欢地上那张桌子。。
怎么办? 貌似可以在allFunc里把 desk相关操作删掉就行了,但是别忘了,还有其他的房间(已经实例化的对象)使用了这个类,牵一发而动全身啊。
没办法,只能重新建立一个复杂度和allFunc一个的类了,只是为了删除一个功能, 这就是多重继承的局限性。
装饰器模式可以很好的应用在许多功能组合的场景,大概的可以描述为 一个新的类 = 旧的类 + 新操作。
看一个经典的水果篮子的例子
class Basket{
public:
virtual void show() = 0;
//普通函数, 既继承接口,又继承具体实现
int &getValue(){
return value;
}
int value = 0;
};
class OriginBasket:public Basket{
public:
void show(){
std::cout<<"this is origin basket, value = "<<value<<std::endl;
}
};
class AppleBasket:public Basket{
public:
AppleBasket(Basket *b){
basket = b;
}
void show(){
basket->show();
std::cout<<"this is apple basket, value = "<<value<<std::endl;
}
private:
Basket* basket;
};
class PearBasket:public Basket{
public:
PearBasket(Basket *b){
basket = b;
}
void show(){
basket->show();
std::cout<<"this is pear basket, value= "<<value<<std::endl;
}
private:
Basket* basket;
};
Basket * AppleAndPear = new PearBasket(
new AppleBasket(new
OriginBasket));
AppleAndPear->show();
显示结果
this is origin basket, value = 0
this is apple basket, value = 0
this is pear basket, value= 0
可以看出,装饰类是在已有类的基础上包裹一次得到的,很像一个函数列,需要什么功能,只需要将它们组合起来就得到了。
小插曲:
AppleAndPear->getValue() = 20;
AppleAndPear->show();
//打印结果
this is origin basket, value = 0
this is apple basket, value = 0
this is pear basket, value= 20
发现origin里的value值没有改,想来也是,三个类里都有分别从基类中继承得到了一个value值,也都有getValue方法来改变这个值,但是从高级类指针pear baskt是无法访问到 origin basket 的值的,只能改变自己的value,层级关系如下:
有人想,对象数据成员里有低级对象的指针啊,可以根据指针访问啊。
AppleAndPear->basket->basket->getValue() = 20;
如果非要访问也不是不可以,那么要突破两个障碍。
- 这是个private 成员, 你把它弄成public就失去了封装的意义了。
- AppleAndPear->basket 指针在类中被声明为 Basket* 类型,通过它去访问 basket变量是行不通的,当然你可以把它强制转换为 AppleBasket* 类型的,但是一来不提倡将基类指针强制转换为派生类,二来,如果层级数量很多会变得非常复杂。