装饰者模式
不喜欢长篇大论的,可以直接看文章最后面的一张图和我自己实现的代码。
装饰者模式
在某些情况我们可能会“过度的使用继承来拓展对象的功能”,由于继承为类型引入的静态特质,使得这种拓展方式缺乏灵活性;并且随着子类的增多(拓展功能的增多),
各个子类的组合(拓展功能的组合)会导致子类的膨胀。
如何使得“对象功能的拓展能够根据需求来动态实现”,同时避免“拓展功能的增加”导致子类的膨胀的问题? 从而使得任何“功能拓展变化”所导致的影响降为最低?
定义
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比子类的继承更加灵活(消除重复代码 和减少子类的个数)。
看看如下一段代码:
// 操作
class Stream {
public:
virtual char Read(int number) = 0;
virtual void Seek(int Position) = 0;
virtual void Write(char* data, int len) = 0;
virtual ~Stream();
};
// 主体类
class FileStream : public Stream {
public:
virtual char Read(int number) {
// 读文件流
}
virtual void Seek(int Position) {
// 定位文件流
}
virtual void Write(char* data, int len) {
// 写文件流
}
};
class NetworkStream : public Stream {
public:
virtual char Read(int number) {
// 读文件流
}
virtual void Seek(int Position) {
// 定位文件流
}
virtual void Write(char* data, int len) {
// 写文件流
}
};
class MemoryStream : public Stream {
public:
virtual char Read(int number) {
// 读文件流
}
virtual void Seek(int Position) {
// 定位文件流
}
virtual void Write(char* data, int len) {
// 写文件流
}
}
目前的关系如图所示
如果现在要有新的需求,比如加密和缓存,那么对上述的主体进行继承,那么代码就会写成这样。
class CryptoFileStream : public FileStream {
public:
virtual char Read(int number) {
// ...加密
FileStream::Read(number);
}
virtual void Seek(int Position) {
//... 加密
FilerStream::Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
FileStream::Write(data, len);
}
};
class CryptoNetworkStream : public NetworkStream {
public:
virtual char Read(int number) {
// ...加密
NetWorkStream::Read(number);
}
virtual void Seek(int Position) {
//... 加密
NetworkStream::Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
NetworkStream(data, len);
}
};
class CryptoMemoryStream : public MemoryStream {
public:
virtual char Read(int number) {
// ...加密
MemoryStream::Read(number);
}
virtual void Seek(int Position) {
//... 加密
MemoryStream::Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
MemoryStream(data, len);
}
};
class BufferFileStream : public FileStream {
public:
virtual char Read(int number) {
// ...缓存
FileStream::Read(number);
}
virtual void Seek(int Position) {
//... 缓存
FilerStream::Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
FileStream::write(data, len);
}
};
class BufferNetworkStream : public NetworkStream {
public:
virtual char Read(int number) {
// ...缓存
NetWorkStream::Read(number);
}
virtual void Seek(int Position) {
//... 缓存
NetworkStream::Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
NetworkStream::Write(data, len);
}
};
class BufferMemoryStream : public MemoryStream {
public:
virtual char Read(int number) {
// ...缓存
MemoryStream::Read(number);
}
virtual void Seek(int Position) {
//... 缓存
MemoryStream::Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
MemoryStream::Write(data, len);
}
};
现在这个结构图变成了这样,
这里面除了之前的主体类,操作,还对每种主机类进行拓展,
其实想想也知道,如果后续的需求越来越多,那么类的数量将会急剧膨胀。
假设基类1个,主体类n个,拓展功能m个,那么总共的的类将有 1+n+n*m!/2
,这里将拓展功能可以进行排列组合。
那么可以怎么做呢?
一个神奇的操作 “继承转组合”,我们想想也知道都是继成基类或者上层的父类。
这里我不打算一步到位,我一步一步一步的写
首先将拓展功能部分由继承转成组合,组合是为了运用到多态的特质,那么就应该使用父类的指针。那么现在代码就变成了,
class CryptoFileStream {
private:
FileStream* stream = nullptr;
public:
virtual char Read(int number) {
// ...加密
stream->Read(number);
}
virtual void Seek(int Position) {
//... 加密
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
stream->write(data, len);
}
};
class CryptoNetworkStream {
private:
NetworkStream* stream = nullptr;
public:
virtual char Read(int number) {
// ...加密
stream->Read(number);
}
virtual void Seek(int Position) {
//... 加密
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
stream->Write(data, len);
}
};
class CryptoMemoryStream {
private:
MemoryStream* stream = nullptr;
public:
virtual char Read(int number) {
// ...加密
stream->Read(number);
}
virtual void Seek(int Position) {
//... 加密
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
stream->Write(data, len);
}
};
class BufferFileStream {
private:
FileStream* stream = nullptr;
public:
virtual char Read(int number) {
// ...缓存
stream->Read(number);
}
virtual void Seek(int Position) {
//... 缓存
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
stream->Write(data, len);
}
};
class BufferNetworkStream {
private:
NetworkStream* stream = nullptr;
public:
virtual char Read(int number) {
// ...缓存
stream->Read(number);
}
virtual void Seek(int Position) {
//... 缓存
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
stream->Write(data, len);
}
};
class BufferMemoryStream {
private:
MemoryStream* stream = nullptr;
public:
virtual char Read(int number) {
// ...缓存
stream->Read(number);
}
virtual void Seek(int Position) {
//... 缓存
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
stream->Write(data, len);
}
};
写到这里我们就发现,不管是CryptoFileStream,CryptoMemoryStream,还是CryptoNetworkStream其中都有个stream指针,而且
这个指针都是继承至基类Stream。那么分别写出来是没必要的,那么我们就应该转化成基类的指针。
class CryptoStream : public Stream{
private:
Stream* stream = nullptr;
public:
virtual char Read(int number) {
// ...加密
stream->Read(number);
}
virtual void Seek(int Position) {
//... 加密
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
stream->write(data, len);
}
};
class BufferStream : public Stream {
private:
Stream* stream = nullptr;
public:
virtual char Read(int number) {
// ...缓存
stream->Read(number);
}
virtual void Seek(int Position) {
//... 缓存
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
stream->Write(data, len);
}
};
目前结构图就变成
我们看到上图中BufferStream CryptoStream中都有包含Stream对象指指针,那么可以将这个Stream指针向上提一步
class Decorator : public Stream {
protected:
Stream* stream = nullptr;
};
class CryptoStream : public Stream{
private:
Decorator* stream = nullptr;
public:
virtual char Read(int number) {
// ...加密
stream->Read(number);
}
virtual void Seek(int Position) {
//... 加密
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 加密
stream->write(data, len);
}
};
class BufferStream : public Stream {
private:
Decorator* stream = nullptr;
public:
virtual char Read(int number) {
// ...缓存
stream->Read(number);
}
virtual void Seek(int Position) {
//... 缓存
stream->Seek(position);
}
virtual void Write(char* data, int len) {
//... 缓存
stream->Write(data, len);
}
};
其实也可以不用提最后的一步, 那么我们统计下一共有1+n+m个类,
注意细节:
1.主要操作就是将继承改为组合, 但是有人会发现还是有继承啊,但是这里的继承是继承基类。
这里主要是为了结构统一,比如都可以用基类的指针表示。
2.使用装饰者模式后,需要在构造函数中初始化Stream 对象指针。构造函数我省略了
大致如下
class BufferStream {
BufferStream(Stream* stm) : stream(stm) {}
// ....
}
- 基类的析构函数一定要带上virtual关键字,道理大家都懂。
具体使用
Stream* s1 = new FileStream(); // FileStream为主体类,
// 一般只继承Stream 不包含Stream对象指针
Stream* s2 = new BufferStream(s1);
s2->Read(number);
// free
// ......
装饰者模式一般是向者一个方向拓展的,这里说的不太准确,反正就是没有明显的分段的趋势。
其次就是有时候,改编的时候,可能基类带有一定的数据,这个数据可以单独提出来也可能不会单独提出来。
这时候基类中的方法就不能生成纯虚函数,道理也很简单,带有纯虚函数的类是没有对象的。
声明成虚函数,就要有相应的实现,不然会报错误。
undefined reference to `vtable for
undefined reference to `typeinfo for
undefined reference to `
如果没有使用纯虚函数,建议子类重写时加上override关键字。
最后看下定义
以组合的方式动态的给一个对象增加一些额外的职责,就增加功能而言Decorator模式比继承方式
更加的灵活(消除重复代码 和 减少子类的个数)。
要点
- 通过采用组合而非继承的方式,Decorator模式实现了运行时动态拓展对象功能,可以根据需要拓展
功能,避免了继承带来的“灵活性差”和“多子类衍生问题”。
2.Decorator 在接口上表现为is-a Component的继承关系, 即是Decorator继承了基类(Component)的所有的接口,但是在实现上又为has-a Component的关系,就是还包含有Component对象的指针。
代码上表现为
class SubClass : public BaseClass {
private:
BaseClass* base = nullptr;
};
- Decorator模式的目的并非是解决“多子类衍生的多继承问题”,Decortor的应用主要是
解决“主体类在多个方向上的拓展功能”——就是“装饰的含义”。
这样一说我上面某处举的例子就不太合适。
最后的关键,我可能画得不太对
- Compotent基类一般是不变的,一般提供一些纯虚函数。
- 主体类可能有多个,都是继承自Compotent基类,是变化的。
- Decorator继承自Compotent基类,包含Compotent 基类指针,一般是不变的。
- ConcreteDecorator继承Decoratot基类,是变化的。
我写了份Decorator模式的代码
#include <iostream>
namespace DesignPatterns {
class Component {
public:
virtual bool Operator() = 0;
virtual ~Component(){}
};
class Decorator : public Component {
protected:
Component* component = nullptr;
public:
explicit Decorator(Component* cmt) : component(cmt) {}
Decorator() = delete;
virtual bool Operator() override;
};
bool Decorator::Operator() {
std::cout << " Decorator Operator begin" << std::endl;
component->Operator();
std::cout << " Decorator Operator end" << std::endl;
}
class ConcreteComponentA : public Component{
public:
virtual bool Operator() override;
};
bool ConcreteComponentA::Operator() {
std::cout << "ConcreteComponentA Operator" << std::endl;
}
class ConcreteDecoratorA : public Decorator {
public:
virtual bool Operator() override;
ConcreteDecoratorA(Component* cmt) : Decorator(cmt) {}
};
bool ConcreteDecoratorA::Operator() {
std::cout << "ConcreteDecoratorA Operator begin" << std::endl;
Decorator::Operator();
std::cout << "ConcreteDecoratorA Operator end" << std::endl;
}
} // namespace DesignPatterns
int main(int argc, char* argv[]) {
using namespace DesignPatterns;
Component* component = new ConcreteComponentA();
Decorator* decorator = new ConcreteDecoratorA(component);
decorator->Operator();
delete component;
delete decorator;
return 0;
}