C++ 简易装饰者模式

59 阅读2分钟

欢迎来到zensorlax的博客

今天分享的是decorator模式

问题

假设你设计了一组Widget, 像是Textfield, 现在要求在Textfield上添加一些decorators

下方是三个思路

执行硬编码
class Textfield:public Widget{
private:
    BorderDecorator border_decorator;
public:
    Textfield(BorderDecorator);
}

然而, 如果当增加decorator, 需要修改Textfield, 随着decorator的增加Textfield变得越发拥挤

三种装饰器, 可以形成

8=i=03(i3)8 = \sum_{i=0}^{3} \binom{i}{3}

下方是3种装饰器下Textfield的类图

decorator.svg 注意这只是Textfield上的装饰, 当涉及一组widgets时, 将会造成‘类爆炸’
显然, 这种这设计非常不合理

抽象接口Decorator

我们发现简单的硬编码会造成上述问题, 那么是否能在Textfield中使用集合管理,
当然可以,但需要保证同类型(也可以使用可以存储不同类型的元组), 显而易见, 我们需要为所有的装饰器抽象出接口
Decorator

class Widget{
    protect:
    vector<Decorator*>decorators_;
}

Interface Decorator{
}

class A :public Decorator{  
}

class B:public Decorator{
}

class C: public Decorator{
}

class Textfield:public Widget{
}

你可能会认为没有什么区别, 然而在这里只需要创建一个类Textfield

decorator.svg

现在假如需要一个ATextfield, 只需编写下列代码

Decorator * a = new A;
Widget *text_fidle = new Textfield;
text_field->addDecorator(a);

但是仔细思考发现了2个问题

  • 核心位于Textfield,需要管理(执行)大量的数据(操作), 稍微违背了单一性原则

  • Textfield类名, 不能准确反映内部的属性和操作并且在建立在实例类的过程中要么不反映类名, 要么直接使用move语义, 重新设置变量名 反反复复, 十分枯燥

decorator模式

为了处理上述的2个问题,引入本篇的主角decorator模式
现在我们思考在‘方案2’中我们在Textfield中包含了decorators的管理对象, 那么是否能颠倒一下, 使用decorators包含Textfield

class Widget {
}

class Textfield : public {
}

class Decorator : public Widget {
   private:
    Widget* widget_;

   public:
    Decorator(Widget*);
    ~Decorator { delete widget_; }
}

class A : public Decorator {
public:
    Decorator(Widget*);
}

class B : public Decorator {
    Decorator(Widget*);
}

class C : public Decorator {
    Decorator(Widget*);
}

在上面的代码中,我将Widget*设置为private, 当然也可以是protect

decorator.svg

在这种模式下, Textfield不再作为中心,组件全部边缘化, 更符合单一性原则并且增加装饰后,不会出现反复move的情况

下方代码是一个例子

Widget *atext_field = new A(new Textfield);
Widget *abtext_field = new B(atext_field);
delete abtext_field;
完整代码

#include <iostream>

using namespace std;

/* Component (interface) */
class Widget {
  public:
    virtual void Draw() = 0;
    virtual ~Widget() {}
};

/* ConcreteComponent */
class TextField : public Widget {
  private:
    int width_;
    int height_;

  public:
    TextField(int width, int height) : width_(width), height_(height) {}

    void Draw() override {
        cout << "TextField: " << width_ << ", " << height_ << '\n';
    }
};

/* Decorator (interface) */
class Decorator : public Widget {
  private:
    Widget *widget_; // reference to Widget

  public:
    explicit Decorator(Widget *widget) : widget_(widget) {}

    void Draw() override { widget_->Draw(); }

    ~Decorator() override { delete widget_; }
};

/* ConcreteDecoratorA */
class BorderDecorator : public Decorator {
  public:
    explicit BorderDecorator(Widget *widget) : Decorator(widget) {}

    void Draw() override {
        Decorator::Draw();
        cout << "   BorderDecorator" << '\n';
    }
};

/* ConcreteDecoratorB */
class ScrollDecorator : public Decorator {
  public:
    explicit ScrollDecorator(Widget *widget) : Decorator(widget) {}

    void Draw() override {
        Decorator::Draw();
        cout << "   ScrollDecorator" << '\n';
    }
};

int main() {
    Widget *widget = new BorderDecorator(
        new BorderDecorator(new ScrollDecorator(new TextField(80, 24))));
    widget->Draw();
    delete widget;
}