设计模式之装饰者模式

2018-09-19  17:11:22

装饰模式(Decorator Pattern)

  装饰模式就是在不改变原有类以及不使用继承的情况下给原有类动态的增加一些额外的职责。

  Tips:不要和建造者模式混淆,建造者模式要求建造的过程是稳定的,但是装饰者模式这个装扮的方式并不是稳定的,有些装扮过程甚至可有可无。例如QQ秀的形象装扮。(感谢大话设计模式的作者给出如此优秀的实例以理解装饰模式)

装饰模式UML图

  

Component:定义一个对象方法的虚基类,可以给这些对象动态的添加职责。 //Component实际上就是需要被装饰的类的基类

ConcreteComponent:定义一个具体的对象,也可以给这个对象添加一些职责。

Decorator:装饰者基类,它和Component是一种聚合关系,就是说它有一个成员是Component类型的。它继承了 Component,从外类来扩展Compoment类的功能,但对于Component来说,它是不需要知道Decorator的存在的。

ConcreteDecorator:就是具体的装饰帝乡,起到给Component添加职责的功能。

代码示例(例子取自大话设计模式)

  解析:这个例子就是模拟了一个人装扮的过程,首先人有头、身体、身高、所处背景这四个基本属性。现在我们要通过装饰者模式,给它添加职责,例如给头带一个圆帽子、背景设置为海滩、身高设置为404、身体上给穿一套礼服,这个是装饰过程就是ConcreteDecorator做的事情,那么具体要装饰谁呢,要装饰的对象就是ConcreteComponent。

1.Component类

#ifndef COMPONENT_H_
#define COMPONENT_H_
#include <string>

class Component
{
public:
    virtual const std::string& getHeadMode() const = 0;
    virtual const std::string& getBodyMode() const = 0;
    virtual const int getHeight() const = 0;
    virtual const std::string& getHairStyle() const = 0;
    virtual const std::string& getName() const = 0;
    virtual void setHeadMode(const std::string strHeadMode) {};
    virtual void setBodyMode(const std::string strBodyMode) {};
    virtual void setHeight(const int iHeight) {};
    virtual void setHairStyle(const std::string strHairStyle){};
    virtual void setName(const std::string strName){};
    Component() = default;
    virtual ~Component() = default;
protected:
    int m_iHeight{0};
    std::string m_strName;
    std::string m_strHeadMode;
    std::string m_strBodyMode;
    std::string m_strHairStyle;
};
#endif
Component

2.具体Component类,这个也是修饰者将要修饰的对象

#ifndef CONCRETECOMPONENT_H_
#define CONCRETECOMPONENT_H_
#include "Component.h"
#include <string>
class ConcreteComponent:public Component
{
public:
    void setName(const std::string strName)
    {
    m_strName = strName;
    }
    void setHeight(const int iHeight)
    {
    m_iHeight = iHeight;
    }
    void setHeadMode(const std::string strHeadMode)
    {
    m_strHeadMode = strHeadMode;
    }
    void setBodyMode(const std::string strBodyMode)
    {
    m_strBodyMode = strBodyMode;
    }
    void setHairStyle(const std::string strHairStyle)
    {
    m_strHairStyle = strHairStyle;
    }
    const std::string& getHeadMode() const override
    {
    return m_strHeadMode;
    }
    const std::string& getBodyMode() const override
    {
    return m_strBodyMode;
    }
    const int getHeight() const override
    {
    return m_iHeight;
    }
    const std::string& getHairStyle() const
    {
    return m_strHairStyle;
    }
    const std::string& getName() const
    {
    return m_strName;
    }
    ConcreteComponent() = default;
    ~ConcreteComponent() = default;
};
#endif
ConcreteComponent

3.装饰者基类:从Component继承而来,并持有一个Component类型的成员指针

#ifndef DECORATOR_H_
#define DECORATOR_H_
#include "Component.h"

class Decorator:public Component
{
public:
    const std::string& getHeadMode() const override
    {
    return m_strHeadMode;
    }
    const std::string& getBodyMode() const override
    {
    return m_strBodyMode;
    }
    const int getHeight() const override
    {
    return m_iHeight;
    } 
    const std::string& getHairStyle() const override
    {
    return m_strHairStyle;
    }
    const std::string& getName() const override
    {
    return m_strName;
    }
    virtual void decorate(Component *) =0;
    virtual void show() = 0;
    Decorator() = default;
    virtual ~Decorator()=default;
protected:
    Component* m_objRole{nullptr};
};
#endif
Decorator

4.具体装饰者,负责装饰具体Component类。本例中这个装饰者负责给小人传衣服。如果还要装饰其它的,例如换个发型,那么就需要在实现一个新的具体装饰者(ConcreteDecorator)类,并在客户端代码里调用它的decorate成员进行装饰。

#ifndef ADDDRESS_H_
#define ADDDRESS_H_
#include "Decorator.h"
#include <iostream>

class AddDress:public Decorator
{
public:
   void decorate(Component *objRole);
   void show();
   AddDress(const std::string strDress):m_strDress(strDress){};
   AddDress() = default;
   ~AddDress() = default;
private:
    std::string m_strDress;
};
#endif

#include "AddDress.h"
void AddDress::decorate(Component* objRole)
{
    m_objRole = objRole;
    if(m_objRole != nullptr)
    {
    std::string strDress = objRole->getBodyMode() + m_strDress;
        objRole->setBodyMode(strDress);
    }
}

void AddDress::show()
{
    if(nullptr != m_objRole)
        std::cout << m_objRole->getName() << "dress a " <<  m_objRole->getBodyMode() << std::endl;
}
AddDress

5.客户端代码,使用2和4中的类,对ConcreteComponent进行装饰。

#include "ConcreteComponent.h"
#include "AddDress.h"

using namespace std;

int main(int argc,char *argv[])
{
    ConcreteComponent objConcreteComponent;
    objConcreteComponent.setName("yang yang");
    objConcreteComponent.setHeight(160);
    
    AddDress objDress("  Skirt");
    objDress.decorate(&objConcreteComponent);
    objDress.show();
    return(1);
}
main

  我觉得使用这个示例来说明装饰者模式略微有些牵强。

  装饰模式是为已有功能动态的添加更多功能的一种方式。当系统需要新功能时,可以通过向旧的类中添加新代码(违背了开放-封闭原则),这些新加的代码通常装饰了原有类的核心职责或者主要行为,在主类中增加新的字段,新的方法和新的逻辑,增加了主类的复杂度,而新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要(功能的动态加入和删除)。针对这种情况,装饰模式提供了比继承更好或者比修改原来相对较好的方式。它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此当需要执行特殊行为时,客户端代码就可以在运行时根据需要有选择地、按顺序的使用装饰功能包装对象。所以装饰模式的优点就是,把类的装饰功能从类中搬移去除,这样可以简化原有的类。并且能有效的把类的核心职责和装饰功能区分开。可以去除相关类中的重复装饰逻辑。

装饰者模式的优缺点

装饰者模式优缺点部分来自:https://blog.csdn.net/liang19890820/article/details/66973836(还没实际使用过,但可以想到它的缺点)

优点:

  • Decorator 模式与继承关系的目的都是要扩展对象的功能,但是 Decorator 可以提供比继承更多的灵活性。
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。

缺点:

  • 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
  • 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
  • 装饰模式是针对抽象构建(Component)类型编程。但是,如果要针对具体构件(ConcreteComponent)编程,应该重新思考应用架构,以及装饰者是否合适。当然也可改变 Component 接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

使用场景:

  • 需要扩展一个类的功能,或给一个类添加附加职责。
  • 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

       

原文地址:https://www.cnblogs.com/ToBeExpert/p/9675830.html