定义:

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。


示例一:装饰模式(通用版)


1. 类图17-5
设计模式 c++版(12)——装饰模式

 

2. 类图说明

  • Component 抽象构件。这是一个接口或抽象类,就是定义我们最核心的对象,也就是最原始的对象,如下面的成绩单。(注:在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当 Component 抽象构建)。
  • ConcreteComponent 具体构件。这是最核心、最原始的接口或抽象类的实现,需要装饰的就是它。
  • Decorator 装饰角色。一般是一个抽象类,实现接口或者抽象方法,它里面可不一定有抽象方法,在他的属性里必然有一个 private 变量指向 Component 抽象构件。
  • 具体装饰角色。ConcreteDecoratorA 和 ConcreteDecoratorB 是两个具体的装饰类,需要把最核心、最原始的东西装饰成其他东西,下面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。

 

3. 代码清单17-4
 

//////////////////////    **********  4. 装饰模式(通用版),代码清单17-4:***************//

class Component
{
public:
    virtual void    operate() = 0;
};

class ConcreteComponent :public Component
{
public:
    virtual void    operate()   //override
    {
        qDebug() << "do something";     
    }
};

class Decorator :public Component
{
public:
    Decorator(Component* component)
    {
        this->m_component = component;
    }
    virtual void    operate()   //override
    {
        this->m_component->operate();
    }

private:
    Component   *m_component;
};

class ConcreteDecorator1:public Decorator
{
public:
    ConcreteDecorator1(Component* component):Decorator(component){}
    virtual void    operate()   //override
    {
        this->method1();
        Decorator::operate();
    }
private:
    void    method1(){qDebug() << "method1"; }
};

class ConcreteDecorator2:public Decorator
{
public:
    ConcreteDecorator2(Component* component):Decorator(component){}
    virtual void    operate()   //override
    {
        this->method2();
        Decorator::operate();
    }
private:
    void    method2(){qDebug() << "method2"; }
};

int main()
{
    Component *component  = new ConcreteComponent();
    Component *component2 = new ConcreteDecorator1(component);
    Component *component3 = new ConcreteDecorator2(component);
    component2->operate();
    component3->operate();
    
    return 0;
}



示例二:成绩单(初设计)

1. 类图17-1
设计模式 c++版(12)——装饰模式


2. 类图说明

一个成绩单的抽象类,一个四年级的成绩单实现类


3. 家长查看成绩单类图17-2
设计模式 c++版(12)——装饰模式


4. 代码清单


////////////////////    **********  1. 成绩单,代码清单17-1:***************//

class SchoolReport
{
public:
    virtual void    report() = 0;
    virtual void    sign()   = 0;
};

class FouthGradeSchoolReport: public SchoolReport
{
public:
    virtual void    report()
    {
        qDebug() << "Chinese : 62";
        qDebug() << "Math : 65";
        qDebug() << "English : 63";
    }
    virtual void    sign()
    {
        qDebug() << "sign!";
    }
};

int main()
{
    SchoolReport *sr = new FouthGradeSchoolReport();
    sr->report();
    
    return 0;
}

 

5. 问题:成绩太低,不给签


6. 改善:把成绩单做一个封装,封装分两步来实现

  • 汇报最高成绩
  • 汇报排名情况

 

示例三:成绩单(改善版:添加装饰)

 

1. 类图17-3
设计模式 c++版(12)——装饰模式

 

2. 类图说明

通过直接增加了一个子类,重写 report 方法

 

3. 代码清单17-2

//////////////////////    **********  2. 成绩单(修饰后),代码清单17-2:***************//

class SchoolReport
{
public:
    virtual void    report() = 0;
    virtual void    sign()   = 0;
};

class FouthGradeSchoolReport: public SchoolReport
{
public:
    virtual void    report()
    {
        qDebug() << "Chinese : 62";
        qDebug() << "Math : 65";
        qDebug() << "English : 63";
    }
    virtual void    sign()
    {
        qDebug() << "sign!";
    }
};

class SugarFGSR:public FouthGradeSchoolReport
{
public:
    virtual void    report()
    {
        this->reportHighScore();
        FouthGradeSchoolReport::report();
        this->reportSort();
    }

private:
    void    reportHighScore(){qDebug() << "Math : 65";}
    void    reportSort(){qDebug() << "number: 38";}
};


int main()
{
    SchoolReport *sr = new SugarFGSR();
    sr->report();
    sr->sign();
    
    return 0;
}

 

4. 问题:一旦需要装饰的条件非常多,通过继承解决就很麻烦了

 

示例四:成绩单(再改善:较多修饰情况)

1. 类图17-4
设计模式 c++版(12)——装饰模式


2. 类图说明:

增加一个抽象类和两个实现类,其中 Decorator 的作用是封装 SchoolReport 类,这里类似一个代理,装饰类的作用也就是一个特殊的代理类,真实的执行者还是被代理的角色 FouthGradeSchoolReport


3. 代码清单17-3
 

//////////////////////    **********  3. 成绩单(较多修饰情况),代码清单17-3:***************//


class SchoolReport
{
public:
    virtual void    report() = 0;
    virtual void    sign()   = 0;
};

class FouthGradeSchoolReport: public SchoolReport
{
public:
    virtual void    report()
    {
        qDebug() << "Chinese : 62";
        qDebug() << "Math : 65";
        qDebug() << "English : 63";
    }
    virtual void    sign()
    {
        qDebug() << "sign!";
    }
};

class Decorator:public SchoolReport
{
public:
    Decorator(SchoolReport *report)
    {
        this->m_report = report;
    }
    virtual void    report()
    {
        this->m_report->report();
    }
    virtual void    sign()
    {
        this->m_report->sign();
    }

private:
    SchoolReport   *m_report;
};

class HighScoreDecorator:public Decorator
{
public:
    HighScoreDecorator(SchoolReport *report):Decorator(report){}
    virtual void    report()
    {
        this->reportHighScore();
        Decorator::report();
    }
private:
    void    reportHighScore(){qDebug() << "Math : 65";}
};

class SortDecorator:public Decorator
{
public:
    SortDecorator(SchoolReport *report):Decorator(report){}
    virtual void    report()
    {
        Decorator::report();
        this->reportSort();
    }
private:
    void    reportSort(){qDebug() << "number: 38";}
};

int main()
{
    SchoolReport *sr1 = new FouthGradeSchoolReport();      //原装成绩单 
    SchoolReport *sr2 = new HighScoreDecorator(sr1);       //加了最高分说明的成绩单 
    SchoolReport *sr3 = new SortDecorator(sr1);            //加了成绩排名的说明

    sr3->report();
    sr3->sign();

    
    return 0;
}

 

五、装饰模式的应用

1. 优点

  •  装饰类和被装饰类可以独立发展,而不会相互耦合。也就是说,Component 类无需知道 Decorator 类,Decorator 类是从外部来扩展 Component 类的功能,而 Decorator 也不用知道具体的构件。
  •  装饰模式是继承关系的一个替代方案。 我们看装饰类  Decorator ,不管装饰多少层,返回的对象还是 Component ,实现的还是is-a关系。
  •  装饰模式可以动态地扩展一个实现类的功能。

 

2. 缺点

多层的装饰是比较复杂的,需要尽量减少装饰类的数量,以便降低系统的复杂度


3. 使用场景:

 需要扩展一个类的功能,或给一个类增加附加功能。
 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
 需要为一批的兄弟类进行改装或加装功能,首选装饰模式。

 


六、最佳实践

  • 装饰模式是对继承有力的补充。要知道继承不是万能的,继承可以解决实际问题,但是在项目中要考虑诸如易维护、易扩展、易服用等,而且在一些情况下,如果用继承就会增加许多子类,而且灵活性非常差,当然也不容易维护,也即,装饰模式可以替代继承,解决我们类膨胀的问题。
  • 继承是静态的给类增加功能,而装饰模式则是动态地增加功能。
  • 扩展性很好。在一个项目中,会有非常多的因素考虑不到,特别是业务的变更,不时地冒出一个需求,尤其是提出一个令项目大量延迟的需求时,通过装饰模式重新封装一个类,而不时通过继承来完成。
  • 三个继承关系Father、Son、GrandSon 三个类,要在Son类上增加功能时如果修改 Son类中的方法是工作量很大的方式。可以通过建立 SonDecorator类来修饰 Son,相当于创建了一个新的类,这个对原有程序没有变更,通过扩展很好地完成了这次变更。

 


参考文献《秦小波. 设计模式之禅》(第2版) (华章原创精品) 机械工业出版社

相关文章:

猜你喜欢
相关资源
相似解决方案