decorator模式
参考资料
• 维基百科:https://en.wikipedia.org/wiki/Decorator_pattern
Decorator模式简介
GoF:Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
GoF:该模式可以动态地为一个对象附加上额外的职责。装饰器为扩展功能的子类化行为提供一个灵活的替换方案。
Wikipedia:In object-oriented programming, the decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
Wikipedia:在面向对象编程中,装饰器模式(通常也称为包装器模式,这种叫法也被在适配器模式中使用)是一种为一个单独的对象静态或动态地添加对象行为,同时不影响同类生成的其他对象的行为的设计模式。装饰器模式通常在遵循单一责任原则的设计中十分有用,这是由于它允许功能在两个类之间进行拆分。
百度百科:23种设计模式之一,英文叫Decorator Pattern,又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
Decorator模式详解
• 设计意图
Decorator设计模式可以用于静态地扩展一个特定对象的功能,或者在某些场景可以在运行期扩展一个特定对象的功能,而不影响与该对象的同类型(同一个类)的其他对象。Decorator模式可以在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
• UML类图
• 结构说明
▶ 抽象构件角色 - Component
GoF:Defines the interface for objects that can have responsibilities added to them dynamically.
为真实对象定义可以动态添加功能的接口。
▶ 具体构件角色 - Concrete Component
GoF:Defines an object to which additional responsibilities can be attached.
定义一个可以附加额外功能的对象类型。
▶ 装饰角色 - Decorator
GoF:Maintains a reference to a Component object and defines an interface that conforms to Component's interface.
维护一个指向Component对象的引用,并且定义和Component一致的接口。
▶ 具体装饰角色 - Concrete Decorator
GoF:Adds responsibilities to the component.
为Component添加具体的功能。
• 模式特点
• 装饰对象和真实对象具有相同的接口,这样用户对象就能以与真实对象交互的相同方式和装饰对象交互。
• 装饰对象包含一个真实对象的引用。
• 装饰对象接受来自用户对象的请求,并把这些请求转发给真实的对象。
• 在同一个真实对象之上可以堆叠多个装饰对象,每次堆叠可以为重写函数添加新的功能点。因此我们可以一条终于原对象的装饰对象链。
装饰器链
在我看来,装饰器模式迷人之处在于可以为一个对象形成一个装饰器链,该装饰器链上每一个结点只负责自己“装饰”的功能点。这种特点使得我们可以不断调整一个对象的职能,可以任意地添加和删除所需的功能。能够这样实现装饰器链的原因很简单,无论是具体的Component还是具体的Decorator,都重写了规约接口operation;并且Decorator和Component是处于同一继承体系之中,我们可以将Decorator视为是一种特殊的Component,但实际上继承只是为了多态的性质,即只需要持有Component类型的一个reference,而无所谓这个reference是引用具体的Component还是Decorator,都能够正确调用具体的operation接口。
在实际使用中,我们经常可以看到1级装饰器D1中持有实际对象引用,2级装饰器D2中持有装饰器D1对象引用,以此类推。
以下面例子一的情况,我们可以编码如下:
#include <string> #include <iostream> using namespace std; // Window class Window { public: Window() {} virtual ~Window() {} public: virtual void Draw() = 0; virtual string GetDescription() = 0; }; // SimpleWindow class SimpleWindow : public Window { public: SimpleWindow() {} virtual ~SimpleWindow() {} public: virtual void Draw() { cout << "SimpleWindow::Draw()" << endl; } virtual string GetDescription() { return "Simple Window"; } }; // WindowDecorator class WindowDecorator : public Window { protected: Window &m_rWindowToBeDecorated; public: WindowDecorator(Window &rWindowToBeDecorated) : m_rWindowToBeDecorated(rWindowToBeDecorated) {} virtual ~WindowDecorator() {} public: virtual void Draw() { m_rWindowToBeDecorated.Draw(); } virtual string GetDescription() { return m_rWindowToBeDecorated.GetDescription(); } }; // VerticalScrollBarDecorator class VerticalScrollBarDecorator : public WindowDecorator { public: VerticalScrollBarDecorator(Window &rWindowToBeDecorated) : WindowDecorator(rWindowToBeDecorated) {} virtual ~VerticalScrollBarDecorator() {} public: virtual void Draw() { WindowDecorator::Draw(); DrawVerticalScrollBar(); } virtual string GetDescription() { return WindowDecorator::GetDescription() + ", including vertical scrollbars"; } private: void DrawVerticalScrollBar() { cout << "VerticalScrollBarDecorator::DrawVerticalScrollBar()" << endl; } }; class HorizontalScrollBarDecorator : public WindowDecorator { public: HorizontalScrollBarDecorator(Window &rWindowToBeDecorated) : WindowDecorator(rWindowToBeDecorated) {} virtual ~HorizontalScrollBarDecorator() {} public: virtual void Draw() { WindowDecorator::Draw(); DrawHorizontalScrollBar(); } virtual string GetDescription() { return WindowDecorator::GetDescription() + ", including horizontal scrollbars"; } private: void DrawHorizontalScrollBar() { cout << "HorizontalScrollBarDecorator::DrawHorizontalScrollBar()" << endl; } }; int main() { SimpleWindow window; VerticalScrollBarDecorator decorated1(window); HorizontalScrollBarDecorator decorated2(decorated1); decorated2.Draw(); cout << decorated2.GetDescription() << endl; return 1; }
输出结果为:
Decorator模式举例
• 例子一
这个例子出自于Wikipedia:https://en.wikipedia.org/wiki/Decorator_pattern
以一个窗口系统中的窗口为例。为了能够使窗口内容进行滚动显示,用户可能希望在窗口中添加垂直或水平方向上的滚动条控件来实现功能。假设所有窗口都由Window这个类实例来显示,并且初始Window类并没有添加滚动条的功能。一种做法可以创建ScrollingWindow子类来提供带滚动条的窗口,另一种做法是创建一个ScrollingWindowDecorator装饰器来为已有的Window类实例来实现功能。到目前为止,两种解决方案都是可行的。
假设用户同时也希望窗口中能增加边框显示的功能,并且初始Window类并没有支持。ScrollingWindow窗口类此时就产生了问题,因为它实际上是创建了一个新的窗口类型。如果用户希望给大多数窗口增加边框显示,而不是所有窗口,那么我们势必需要创建类似WindowWithBorder和ScrollingWindowWithBorder等的窗口类。这个问题在每增加一种新的窗口特效或窗口类型时会变得更加糟糕。如果使用装饰器的解决方案,我们只需要简单地在运行期创建一个新的BorderedWindowDecorator装饰器即可,那么我们可以使用ScrollingWindowDecorator和BorderedWindowDecorator装饰器来装饰已有的窗口实例。如果用户希望一个功能能被所有窗口所使用,那么我们修改基类Window来实现,反之,修改基类有时是没有可能的,或不合法的,或不方便的行为。
• 例子二
这个例子出自于GoF,与Wikipedia的例子十分类似。
例如,假设我们拥有一个TextView控件来在窗口中显示文本,在默认情况下,TextView控件是没有滚动功能的,因为用户并不是一直都需要文本滚动功能。当需要为TextView实例增加文本滚动功能,我们只需要使用ScrollDecorator装饰器来增添功能。假设我们需要为TextView控件加上一个粗的黑色边框来装饰控件,那么我们要做的也只是使用BorderDecorator装饰器来实现。在这种思路下,我们只需要简单地在TextView对象上组合各种装饰器,就能产生我们所需要的结果。
下面的对象关系图显示了怎样在TextView对象上组合使用BorderDecorator和ScrollDecorator装饰器对象来产生一个带边框和滚动条的文本视图: