in Software engineering 设计模式 ~ read.
【设计模式】装饰模式

【设计模式】装饰模式

将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,这个嵌入的对象成为装饰器(Decorator)

decorator

优劣

GOOD:当你向旧的类中添加新代码时,一般是为了添加核心职责或主要行为。而当需要加入的仅仅是一些特定情况下才会执行的特定的功能时(简单点就是不是核心应用的功能),就会增加类的复杂度。装饰模式就是把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择地、按顺序地使用装饰功能包装对象

优点

• 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
• 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
• 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
• 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

缺点

• 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
• 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

使用环境

• 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
• 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
• 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类).

参考代码

#include <string>
#include <iostream>
using namespace std;
//人
class Person
{
private:
    string m_strName;
 
public:
    Person(string strName)
    {
        m_strName = strName;
    }
    Person() {}
    virtual void Show()
    {
        cout << "装扮的是:" << m_strName << endl;
    }
};
//装饰类
class Finery : public Person
{
protected:
    Person * m_component;
 
public:
    void Decorate(Person *component)
    {
        m_component = component;
    }
    virtual void Show()
    {
        m_component->Show();
    }
};
//T 恤
class TShirts : public Finery
{
public:
    virtual void Show()
    {
        cout << "T Shirts" << endl;
        m_component->Show();
    }
};
//裤子
class BigTrouser : public Finery
{
public:
    virtual void Show()
    {
        cout << " Big Trouser" << endl;
        m_component->Show();
    }
};
//客户端
int main()
{
    Person *p = new Person("小李");
    BigTrouser *bt = new BigTrouser();
    TShirts *ts = new TShirts();
    bt->Decorate(p);  // 用裤子装饰小李
    ts->Decorate(bt);  // 用T恤装饰 穿裤子的小李
    ts->Show();    // ts->Show()   bt->Show()   p->Show()
    return 0;
}