当系统需要新功能的时候,是向旧系统的类中添加新代码,而不是更改已有代码,这符合开放封闭原则,而装饰模式可以很好的做到这一点,装饰模式是把类中的装饰功能从类中抽象出去,这样可以简化原有的类,有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。
装饰模式的UML类图如下:
本UML类图借用《大话设计模式》的UML类图,可能上面的解释部分不是很好理解,这里再重新用大白话解释一下:
Component类:是一个抽象的类,是被装饰者的抽象类。比如苹果手机,华为手机是我们要装饰的目标,是被装饰者,那么可以将它们抽象为一个手机类,即Component类,然后苹果手机类和华为手机类继承手机类
ConcreteComponent类:是具体的被装饰者类,比如上述的苹果手机和华为手机
Decorator类:是装饰抽象类
ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰类,通过它们给被装饰对象进行装饰。
而装饰模式的具体用法通过一两句话实在是难以描述,请通过下面的例子来进行理解。
#include<iostream>
using namespace std;
//抽象类,对应UML类图component
//被装饰者可能不止一个,它是可能的变化,因此先把他们抽象出一个类来,符合开放-封闭原则
class Phone
{
public:
Phone() {}
virtual ~Phone() {}
virtual void ShowDecorate() = 0;
};
//具体的手机类,其对象就是要修饰的对象,对应UML类图concreteComponent
class iPhone : public Phone
{
private:
string m_name; //手机名称
public:
iPhone(string name) : m_name(name) {}
~iPhone() {}
void ShowDecorate()
{
cout << m_name << "的装饰" << endl;
}
};
//抽象装饰类,对应UML类图Decorator类
//装饰方法可能不止一种,因此先将它们抽象出一个类来,符合开放-封闭原则
class DecoratorPhone : public Phone
{
private:
Phone* m_phone; //该指针所指对象就是要装饰的手机对象
public:
DecoratorPhone(Phone* phone) : m_phone(phone) {}
virtual void ShowDecorate()
{
m_phone->ShowDecorate();
}
};
//具体的装饰类A,对应UML类图ConcreteDecoratorA
class DecoratorPhoneA : public DecoratorPhone
{
public:
DecoratorPhoneA(Phone* phone) : DecoratorPhone(phone) {}
//因为创建装饰类A的对象时,要将被修饰对象传递给该构造函数,因此装饰类A中指针m_phone指向的是被修饰对象
void ShowDecorate()
{
DecoratorPhone::ShowDecorate(); AddDecorate();
}
/*先调用抽象装饰类中的showDecorate函数,装饰类中的showDecorate函数会去调用m_phone所指对象的showDecorate函数,即被装饰对象的showDecorate函数,当被装饰对象中的showDecorate函数调用完后,调用装饰函数*/
private:
void AddDecorate()
{
cout << "增加挂件" << endl;
} //增加的装饰
};
//具体的装饰类B,对应UML类图的ConcreteDecoratorB
class DecoratorPhoneB : public DecoratorPhone
{
public:
DecoratorPhoneB(Phone* phone) : DecoratorPhone(phone) {}
//在创建装饰类B的对象时会把装饰类A的对象作为参数传入该构造函数,故装饰B类中m_phone指针指向的是装饰A类对象
void ShowDecorate()
{
DecoratorPhone::ShowDecorate(); AddDecorate();
}
/*
* 1.装饰类B的showDecorate先调用抽象装饰类中的showDecorate函数,再调用装饰B类的装饰函数
* 2抽象装饰类中的showDecorate函数会去调用m_phone指针所指对象的showDecorate函数,即装饰类A的showDecorate函数
* 3装饰类A的showDecorate函数会先去调用抽象装饰类中的showDecorate函数,再去调用装饰A类的装饰函数
*4抽象装饰类中的showDecorate又会去调用m_phone所指对象的showDecorate函数,即被装饰对象的showDecorate函数,showDecorate调用完成后,
回到第3步去调用A的装饰函数,调用完成后,装饰类A中的showDecorate函数调用完毕,那么第2步也就完成,回到第一步去调用装饰B类的装饰函数,调用完成后,装饰B的showDecorate函数调用完成
*/
private:
void AddDecorate()
{
cout << "屏幕贴膜" << endl;
} //增加的装饰
};
int main()
{
Phone* iphone = new iPhone("6300");
Phone* dpa = new DecoratorPhoneA(iphone); //装饰,增加挂件
Phone* dpb = new DecoratorPhoneB(dpa); //装饰,屏幕贴膜
dpb->ShowDecorate();
delete dpa;
delete dpb;
delete iphone;
return 0;
}
仔细理清上述代码,就会对装饰模式有一个更加清晰的认识,个人感觉,在不断地给装饰对象添加功能的时候,其背后的逻辑就像是一个套娃…
学习一个设计模式,除了学习它的核心思想以外,我们还需要学习它的优缺点以及使用场景。
优点:
- Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性,因为装饰模式可以动态添加,动态取消功能。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:
- 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
- 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
适用场景:
以下情况使用Decorator模式
- 需要扩展一个类的功能,或给一个类添加附加职责。
- 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。