一、概述
动态地给一个对象添加一些额外的职责(不重要的功能,只是偶然一次要执行) ,就增加功能来说,装饰模式比生成子类更为灵活。建造过程不稳定,按正确的顺序串联起来进行 控制。
1.结构
-
部件(Component)声明封装器和被封装对象的公用接口。
-
具体部件(Concrete Component)类是被封装对象所属的类。它定义了基础行为,但装饰类可以改变这些行为。
-
基础装饰(Decorator)类拥有一个指向被封装对象的引用成员变量。该变量的类型应当被声明为通用部件接口,这样它就可以引用具体的部件和装饰。装饰基类会将所有操作委派给被封装的对象。
-
具体装饰类(Concrete Decorators) 定义了可动态添加到部件的额外行为。具体装饰类会重写装饰基类的方法,并在调用父类方法之前或之后进行额外的行为。
2.适用场景
-
在不影响其他对象的情况下,以多态、透明的方式给单个对象添加职责。
-
用继承来扩展对象行为的方案难以实现或者根本不可行,可以使用该模式。
3.优缺点
优点:
-
装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。
-
单一职责原则。你可以将实现了许多不同行为的一个大类拆 分为多个较小的类。
-
装饰器模式完全遵守开闭原则。
缺点:
-
在封装器栈中删除特定封装器比较困难。
-
实现行为不受装饰栈顺序影响的装饰比较困难。
-
各层的初始化配置代码看上去可能会很糟糕。
二、实例
#include<iostream>
#include<string>
using namespace std;
// 部件: 是具体部件和装饰类的共同基类, 在C++中实现成抽象基类
class DataSource
{
public:
virtual void writeData(string data) = 0;
};
// 具体组件提供操作的默认实现, 这些类在程序中可能会有几个变体
class FileDataSource :public DataSource {
private:
string file_name_;
public:
explicit FileDataSource(string file_name):file_name_(file_name){}
void writeData(string data) override {
printf("写入文件%s中: %s\n", file_name_.c_str(), data.c_str());
return;
}
};
/* 装饰基类和其他组件遵循相同的接口。
* 该类的主要任务是定义所有具体装饰的封装接口。
* 封装的默认实现代码中可能会包含一个保存被封装组件的成员变量
* 并且负责对其进行初始化。
*/
class DataSourceDecorator : public DataSource {
protected:
DataSource* data_source_;
public:
explicit DataSourceDecorator(DataSource* ds) : data_source_(ds) {}
void writeData(string data) override {
data_source_->writeData(data);
}
};
// 压缩装饰器
class CompressionDecorator : public DataSourceDecorator {
public:
DataSourceDecorator::DataSourceDecorator;
void writeData(string data) override {
// 1. 对传递数据进行压缩(这里仅简单实现)
data = "已压缩(" + data + ")";
// 2. 将压缩后数据传递给被封装对象 writeData(写入数据)方法
data_source_->writeData(data);
return;
}
};
// 加密装饰器
class EncryptionDecorator : public DataSourceDecorator {
public:
DataSourceDecorator::DataSourceDecorator;
void writeData(string data) override {
// 1. 对传递数据进行加密(这里仅简单实现)
data = "已加密(" + data + ")";
// 2. 将加密后数据传递给被封装对象 writeData(写入数据)方法
data_source_->writeData(data);
return;
}
};
int main()
{
FileDataSource* source1 = new FileDataSource("stdout");
// 将明码数据写入目标文件
source1->writeData("tomocat");
// 将压缩数据写入目标文件
CompressionDecorator* source2 = new CompressionDecorator(source1);
source2->writeData("tomocat");
// 将压缩且加密数据写入目标文件
EncryptionDecorator* source3 = new EncryptionDecorator(source2);
source3->writeData("tomocat");
delete source1;
delete source2;
delete source3;
}