装饰器模式(Decorator Pattern)
在软件设计中 , 如果职责划分不清晰 , 使用继承得到的结果往往是随着需求的变化 , 子类急剧的膨胀 ,同时充斥
重复的代码,这个时候的关键是划分职责。
动态(组合)地给一个对象增加一些额外的职责 . 就增加功能而言 , Decorator模式生成子类(继承)更为灵活,
(消除重复代码 & 减少子类的个数) ------《设计模式》GoF
意图:
动态地向对象添加功能 / 职责
应用场景:
1. 子类拓展功能不可行
2. 向同一类型对象中的某个对象动态添加或删除功能 ,而不影响到其他对象
影响:
1. 动态地向对象添加 / 删除功能 , 使代码调试变得困难
类图:
例如:
一个流文件类作为基类:Stream
class Stream {
public:
virtual char read(int number) = 0;
virtual void seek(int position) = 0;
virtual void write(char data) = 0;
virtual ~Stream(){}
};
在Stream衍生出各种流: FileStream 、NetworkStream …
class Stream {
public:
virtual char read(int number) = 0;
virtual void seek(int position) = 0;
virtual void write(char data) = 0;
virtual ~Stream(){}
};
//主体类
class FileStream : public Stream {
public:
virtual char read(int number) {
//读文件
}
virtual void seek(int position) {
//定位文件
}
virtual void write(char data) {
//写文件
}
};
接下来要给这些流加密 、添加缓冲等功能:
其中的Decorator是对公共部分的抽象。(Stream *stream抽象到Decorator中去)
class CryptoStream : public DecoratorStream { //继承Sream满足read 、 seek 、 write的接口
private:
Stream* stream; // = new xxStream{};
public:
//CryptoStream(Stream* stream):stream(stream){ }
CryptoStream(Stream* stream) :DecoratorStream(stream) { }
virtual char read(int number) {
//额外的加密操作...
stream->read(number);//读文件
}
virtual void seek(int position) {
//额外的加密操作...
stream->seek(position);//定位文件
}
virtual void write(char data) {
//额外的加密操作...
stream->write(data);//写文件
}
};
class BufferedStream : public DecoratorStream { //继承Stream实现接口
private:
Stream* stream; // = new xxStream{};
public:
virtual char read(int number) {
//额外的缓冲操作...
stream->read(number);//读文件
}
virtual void seek(int position) {
//额外的缓冲操作...
stream->seek(position);//定位文件
}
virtual void write(char data) {
//额外的缓冲操作...
stream->write(data);//写文件
}
};
Decorator的类: 其中继承Stream是为了实现其 read 、write的接口 。组合是为了去实现传进来的各种Stream的read、write的读写操作 (此处一定要明白)
class DecoratorStream : public Stream {
protected:
Stream* stream;
DecoratorStream() = default;
DecoratorStream(Stream* stream) :stream(stream) {}
};
运行实例:
void process() {
//运行时装配
FileStream* s1 = new FileStream(); //创建文本对象
CryptoStream* s2 = new CryptoStream(s1); //对文件s1进行加密
BufferedStream{};
};
总结:
-
采用组合而非继承的手法 , Decorator模式实现了在运行时动态拓展的能力,
而且可以更具需要拓展多个功能 。 避免了使用继承带来的“灵活性差” 和 “多个子类衍生问题” -
Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类具有的
接口。但在实现上又表现为has-a Component的组合关系 。 Decorator类又使用了另外一个Component的类 -
Decorator模式的目的并非解决"多子类衍生的多继承"问题,
Decorator模式应用的要点在于解决 “主体类在多个方向上的拓展功能”----是为“装饰”的含义