前言
尊重原创,本系列文本解析部分主要基于Candidate for Master’s Degree School of Computer Wuhan University的K_Eckel([email protected])《设计模式精解-GoF 23 种设计模式解析附 C++实现源码》。为了避免重造轮子,本系列博文对源码在ubuntu16.04下做了验证并上传到了gitee,再次感谢。
如有问题,可邮件联系我([email protected])并共同探讨解决方案。
目录
创建型模式(Creating Pattern)
Factory 模式 | AbstactFactory 模式 | Singleton 模式 | Builder 模式 | Prototype 模式
结构型模式(Structrual Pattern)
Bridge 模式 | Adapter 模式 | Decorator 模式 | Composite 模式 | Flyweight 模式 | Facade 模式 | Proxy 模式
行为型模式(Behavioral Pattern)
Template 模式 | Strategy 模式 | State 模式 | Observer 模式 | Memento 模式 | Mediator 模式 | Command 模式 | Visitor 模式 | Iterator 模式 | Interpreter 模式 | Chain of Responsibility 模式
Decorator 模式简介:
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。
一般有两种方式可以实现给一个类或对象增加行为:
- 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
- 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。
问题
在 OO 设计和开发过程,可能会经常遇到以下的情况:我们需要为一个已经定义好的类添加新的职责(操作),通常的情况我们会给定义一个新类继承自定义好的类,这样会带来一个问题(将在本模式的讨论中给出)。通过继承的方式解决这样的情况还带来了系统的复杂性,因为继承的深度会变得很深。
而 Decorator 提供了一种给类增加职责的方法,不是通过继承实现的,而是通过组合。有关这些内容在讨论中进一步阐述。
模式选择
Decorator 模式典型的结构图为:
图 1:Decorator Pattern 结构图
在 结 构 图 中 , ConcreteComponent 和 Decorator 需 要 有 同 样 的 接 口 , 因 此ConcreteComponent 和 Decorator 有着一个共同的父类。这里有人会问,让 Decorator 直接维护一个指向 ConcreteComponent 引用(指针)不就可以达到同样的效果,答案是肯定并且是否定的。肯定的是你可以通过这种方式实现,否定的是你不要用这种方式实现,因为通过这种方式你就只能为这个特定的 ConcreteComponent 提供修饰操作了,当有了一个新的ConcreteComponent 你 又 要 去 新 建 一 个 Decorator 来 实 现 。 但 是 通 过 结 构 图 中 的ConcreteComponent 和 Decorator 有一个公共基类,就可以利用 OO 中多态的思想来实现只要是 Component 型别的对象都可以提供修饰操作的类,这种情况下你就算新建了 100 个Component 型别的类 ConcreteComponent,也都可以由 Decorator 一个类搞定。这也正是Decorator 模式的关键和威力所在了。
当然如果你只用给 Component 型别类添加一种修饰,则 Decorator 这个基类就不是很必要了。
实现
完整代码示例(code)
Decorator 模式的实现起来并不是十分困难,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 Visual Studio Code,Version: 1.36.1 下测试运行)。
源码gitee地址:点击这里
代码目录结构:
patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Decorator/src$ tree ../
../
├── include
│ └── Decorator.h
└── src
├── Decorator.cpp
└── main.cpp
2 directories, 3 files
Decorator.h:
// Decorator.h
#ifndef _DECORATOR_H_
#define _DECORATOR_H_
class Component {
private:
/* data */
protected:
Component(/* args */);
public:
virtual ~Component();
virtual void Operation();
};
class ConcreteComponent : public Component {
private:
/* data */
public:
ConcreteComponent(/* args */);
~ConcreteComponent();
void Operation();
};
class Decorator : public Component {
private:
/* data */
protected:
Component* _com;
public:
Decorator(Component* com);
~Decorator();
void Operation();
};
class ConcreteDecorator: public Decorator
{
private:
/* data */
public:
ConcreteDecorator(Component* com);
~ConcreteDecorator();
void Operation();
void AddedBehavior();
};
#endif //~_DECORATOR_H_
Decorator.cpp:
// Decorator.cpp
#include "../include/Decorator.h"
#include <iostream>
Component::Component(/* args */) {}
Component::~Component() {}
void Component::Operation() {}
ConcreteComponent::ConcreteComponent(/* args */) {}
ConcreteComponent::~ConcreteComponent() {}
void ConcreteComponent::Operation() { printf("ConcreteComponent operation... \n"); }
Decorator::Decorator(Component* com) { this->_com = com; }
Decorator::~Decorator() { delete _com; }
void Decorator::Operation() {}
ConcreteDecorator::ConcreteDecorator(Component* com):Decorator(com) {}
ConcreteDecorator::~ConcreteDecorator() {}
void ConcreteDecorator::AddedBehavior(){
printf("ConcreteDecorator::AddedBehavior... \n");
}
void ConcreteDecorator::Operation(){
_com->Operation();
this->AddedBehavior();
}
main.cpp:
// main.cpp
#include <iostream>
#include "../include/Decorator.h"
using namespace std;
int main(int argc, char* argv[]) {
Component* com = new ConcreteComponent();
Decorator* dec = new ConcreteDecorator(com);
dec->Operation();
delete dec;
return 0;
}
代码说明
Decorator 模 式 很 简 单 ,代 码 本 身 没有 什 么 好 说明 的 。 运 行示 例 代 码 可以 看 到 ,ConcreteDecorator 给 ConcreteComponent 类添加了动作 AddedBehavior。
编译运行结果:
patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Decorator/src$ g++ *.cpp -std=c++11
patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Decorator/src$ ./a.out
ConcreteComponent operation...
ConcreteDecorator::AddedBehavior...
patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Decorator/src$
讨论
Decorator 模式和 Composite 模式有相似的结构图,其区别在 Composite 模式已经详细讨论过了,请参看相应文档。另外 GoF 在《设计模式》中也讨论到 Decorator 和 Proxy 模式有很大程度上的相似,初学设计模式可能实在看不出这之间的一个联系和相似,并且它们在结构图上也很不相似。实际上,在本文档“模式选择”中分析到,让 Decorator 直接拥有一个 ConcreteComponent 的引用(指针)也可以达到修饰的功能,大家再把这种方式的结构图画出来,就和 Proxy 很相似了!
Decorator 模式和 Proxy 模式的相似的地方在于它们都拥有一个指向其他对象的引用(指针),即通过组合的方式来为对象提供更多操作(或者 Decorator 模式)间接性(Proxy 模式)。但是他们的区别是,Proxy 模式会提供使用其作为代理的对象一样接口,使用代理类将其操作都委托给 Proxy 直接进行。这里可以简单理解为组合和委托之间的微妙的区别了。
Decorator 模式除了采用组合的方式取得了比采用继承方式更好的效果,Decorator 模式还给设计带来一种“即用即付”的方式来添加职责。在 OO 设计和分析经常有这样一种情况:为了多态,通过父类指针指向其具体子类,但是这就带来另外一个问题,当具体子类要添加新的职责,就必须向其父类添加一个这个职责的抽象接口,否则是通过父类指针是调用不到这个方法了。这样处于高层的父类就承载了太多的特征(方法),并且继承自这个父类的所有子类都不可避免继承了父类的这些接口,但是可能这并不是这个具体子类所需要的。而在Decorator 模式提供了一种较好的解决方法,当需要添加一个操作的时候就可 以通过Decorator 模式来解决,你可以一步步添加新的职责。