以下笔记摘录自李建忠老师的<<c++设计模式>>和书籍<<设计模式>>
动机
希望给某个对象而不是整个类添加一些功能,或者说动态的给一个对象添加一些额外的职责
原始代码
1 | class Stream{ |
1 | class FileStream : public Stream |
1 | class CryptoFileStream : public FileStream |
1 | class BufferdFileStream : public FileStream { |
- 类的继承关系及扩展组合方式如下:
- 可以看到随着扩展种类增多,各种组合导致类的数量急剧增加
- 使用代码如下:
1
2
3
4
5//编译时装配
FileStream * s1 = new FileStream();
CryptoFileStream * s1 = new CryptoFileStream();
BufferdFileStream * s2 = new BufferdFileStream();
CryptoBufferdFileStream * s3 = new CryptoBufferdFileStream();
重构: 第一个版本,重要的思路转换
扩展操作类由继承改为组合
1 | class CryptoFileStream |
1 | class CryptoNetworkStream |
1 | class CryptoMemoryStream |
重构: 第二个版本,充分利用抽象类
扩展操作类子类对象指针改为基类指针,通过不同的初始化方式应不同的子类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class CryptoFileStream
{
Stream * stream; // = new FileStream();
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);
//额外的加密操作...
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class CryptoNetworkStream
{
Stream * stream; // = new NetworkStream();
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);
//额外的加密操作...
}
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class CryptoMemoryStream
{
Stream * stream; // = new MemoryStream();
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);
//额外的加密操作...
}
};
重构: 第三个版本,合并简化
第二个重构版本中可以发现这三个类其实是完全一样,以合并为一个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class CryptoStream
{
Stream * stream; // = new FileStream(); or new NetworkStream(); or new MemoryStream();
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);
//额外的加密操作...
}
};为保留基类虚函数的规范,这个类还要继承
Stream类,同时添加了构造函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class CryptoStream : public Stream
{
Stream * stream; // = new FileStream(); or new NetworkStream(); or new MemoryStream();
public:
CryptoStream(Stream * stm) : stream(stm) {}
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模式的一个重要标志
同理
buffered扩展就重构为1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class BufferedStream : public Stream
{
Stream * stream; // = new FileStream(); or new NetworkStream(); or new MemoryStream();
public:
BufferedStream(Stream * stm) : stream(stm) {}
virtual char Read(int number) {
//额外的buffer操作...
stream->Read(number);
}
virtual void Seek(int position){
//额外的buffer操作...
stream->Seek(position);
}
virtual void Write(char data){
//额外的buffer操作...
stream->Write(data);
}
};
重构: 最后的版本,锦上添花
可以发现第三个版本中
CryptoStream和BufferdStream中都有成员指针,那么就可以尝试将其往底层提取一个直接的办法是放置在抽象类
Stream中,但这样对三个主题类而言是多余的所以可以再封装一个类
DecoratorStream1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class DecoratorStream : public Stream
{
Stream * stream;
public:
DecoratorStream(Stream * stm) : stream(stm) {}
};
//扩展操作继承DecoratorStream
class CryptoStream : public DecoratorStream
{
public:
CryptoStream(Stream * stm) : DecoratorStream(stm) {}
};
class BufferedStream : public DecoratorStream
{
public:
BufferedStream(Stream * stm) : DecoratorStream(stm) {}
};使用代码如下:
1
2
3
4
5//运行时装配
FileStream * s1 = new FileStream();
CryptoStream * s2 = new CryptoStream(s1); //针对FileStream加密
BufferdStream * s3 = new BufferdStream(s1); //针对FileStream缓冲
BufferdStream * s4 = new BufferdStream(s2); //针对FileStream既缓冲又加密
总结
- 根源分析:某些情况下”过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质(扩展类中调用子类的
Read,Seek,Write),使得这种扩展方式缺乏灵活性。 - 模式定义:动态(组合)的给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码&减少子类个数)