定义:
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
UML图:
形式:
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
例子:
// 设计模式Demo.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
//要被装饰的对象基类(Component),这里用人物基类表示
class BaseCharacter
{
public:
virtual void func() = 0;
};
//要被修饰的对象(ConcreateComponent),这里用玩家表示
class Player : public BaseCharacter
{
public:
void func() override
{
cout<<"I am Character. I don't have any equipment"<<endl;
}
};
//装饰基类(Decorator),这里用装备基类表示
class EquipmentBase : public BaseCharacter
{
private:
BaseCharacter* m_pCharacter;
public:
void setCharacter(BaseCharacter* c)
{
m_pCharacter = c;
}
//重写function,执行character的func方法
void func() override
{
if (m_pCharacter)
m_pCharacter->func();
}
};
//具体装饰类(ConcreateDecorater),这里用武器表示
class Weapon : public EquipmentBase
{
private:
//增加一个字段
string m_weapon;
public:
void func() override
{
//先执行基类的方法
EquipmentBase::func();
//执行装饰类特有的方法
m_weapon = "But now I have a gun!";
cout<<m_weapon<<endl;
}
};
//具体装饰类(ConcreateDecorater),这里用衣服表示
class Clothes : public EquipmentBase
{
public:
void equipCloth()
{
cout<<"But now I have clothes"<<endl;
}
void func() override
{
EquipmentBase::func();
equipCloth();
}
};
//客户端
int _tmain(int argc, _TCHAR* argv[])
{
BaseCharacter* character = new Player();
Weapon* weapon = new Weapon();
Clothes* clothes = new Clothes();
/*
这里可能有些不太恰当,所说的装备并不是真正的装备,而是装备了装备的玩家
即通过了装备类,使玩家变成了有装备的玩家
*/
//没有装备的玩家
cout<<"Before Equip:"<<endl;
character->func();
//装备武器的玩家
cout<<"After Equip a weapon:"<<endl;
weapon->setCharacter(character);
weapon->func();
//再装备衣服的玩家
cout<<"After Equip clothes:"<<endl;
clothes->setCharacter(weapon);
clothes->func();
system("pause");
return 0;
}
这个例子写的是一个玩家和装备之间的关系,但是稍微有些不恰当,例子中的装备类并非仅仅表示一个装备,由于装备类继承了玩家类,所以这里表示的是装备了装备的玩家。
装饰模式通过装饰类,继承被装饰类,使装饰类自身和被装饰类接口相同。而内部封装了一个被装饰的对象引用(指针),我们可以更加灵活的在装饰类中添加要增加的代码,而且这样支持嵌套装饰,即有层次的装饰。如上面的例子,先装备武器再装备衣服。
装饰模式可以动态的添加功能,就增加功能来说,比生成子类更加灵活。
什么时候使用装饰模式?
1.需要扩展一个类的功能时,装饰模式可以将装饰部分和原本的类的核心功能分开,这样更有利于功能划分。多用组合,少用继承!
2.
需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。像上面的例子,想先穿衣服再拿武器也是可以的,而继承没法做到这点。
4.当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。