我们经常会遇到这种问题,当一个类出现非常多的选择,比如 咖啡,要加 糖、抹茶、香草、牛奶……,那么多调味品,在这种情况下,考虑通过继承的方式是否合理?你会得到 很多种咖啡类,牛奶咖啡、抹茶咖啡、牛奶椰蓉咖啡、香草黑糖咖啡、曲奇巧克力蛋奶咖啡……,好像很不靠谱对吧?
发现了 上面的 子类爆炸问题 后,你可能会问,我需要定义那么多的子类吗? 咖啡就是咖啡,其他的只是酌料而已。
装饰者模式 相当于在类外面加上包装,生成新的类,这样做的好处 一是 减少了子类的数量(没有了组合类),二是可以在运行时动态组合,更加灵活。
还是以 咖啡为例,通过代码来进行说明:
// Caffe抽象基类 - Component
class ICaffe
{
public:
virtual float getPrice();
};
// Caffe实现类 - ConcreteComponent
class Caffe : public ICaffe
{
public:
virtual float getPrice() { return 10; };
};
// Decortaor
class Decorator : public ICaffe
{
public:
Decorator(Icaffe* pComponent)
: m_pComponent(pComponent)
{
}
public:
virtual float getPrice()
{
return m_pComponent->getPrice();
}
protected:
ICaffe* m_pComponent;
};
// ConcreteDecorator
class MochaCaffe : public Decorator
{
public:
MochaCaffe(Icaffe* pComponent)
: Decorator(pComponent)
{
}
public:
virtual float getPrice()
{
return 0.5 + m_pComponent->getPrice();
}
};
装饰者模式 通过相同的抽象基类,能够实现多层的包装,这样做的缺点是 其内容定义往往不是很清晰,就好像我们收到一个程序员朋友寄来的礼物,拆开 天猫的塑料袋,里面是个纸箱子,拆开纸箱子里面是一个盒子,拆开盒子发现里面是一个信封,拆开信封发现里面有张纸,清晰得写着四个大字:“Hello World!”
尽管大家众说纷纭,但作者并不推崇 装饰者模式,相反,在复杂性和可读性之间抉择,我认为这是一个失败的模式,也许下面的实现会更容易理解:
// Caffe类
class Caffe
{
public:
virtual float getPrice() { return 10; }
};
// 修饰组件类
enum Dressing_Type { 抹茶,糖,辣椒,牛奶,香草,巧克力,奶昔,… }
class Dressing
{
public:
Dressing_Type type; // 修饰类型
float price;
};
// 具体实现类
class DressedCaffe : public Caffe
{
public:
virtual float getPrice()
{
// 遍历vector求和
}
protected:
Caffe m_Caffe;
vector<Dressing> m_vecDressing;
};
只需要扩展Dressing的属性定义即可,这里不去比较哪种方案的优劣,其实每个人都有自己的代码习惯,符合自己的才是最好的,PS:设计模式里面真正应用最多的也就那么几种,用之前先要考虑这是否必要,强用设计模式 着实是初学者 一个非常大的误区!