以下笔记摘录自李建忠老师的<<c++设计模式>>和书籍<<设计模式>>

动机

希望给某个对象而不是整个类添加一些功能,或者说动态的给一个对象添加一些额外的职责

原始代码

1
2
3
4
5
6
7
8
class Stream{
public:
virtual char Read(int number) = 0;
virtual void Seek(int position) = 0;
virtual void Write(char data) = 0;

virtual ~Stream() {}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class FileStream : public Stream
{
public:
virtual char Read(int number) {
//读文件流
}
virtual void Seek(int position){
//定位文件流
}
virtual void Write(char data){
//写文件流
}
};

class NetworkStream : public Stream
{
public:
virtual char Read(int number) {
//读网络流
}
virtual void Seek(int position){
//定位网络流
}
virtual void Write(char data){
//写网络流
}
};

class MemoryStream : public Stream
{
public:
virtual char Read(int number) {
//读内存流
}
virtual void Seek(int position){
//定位内存流
}
virtual void Write(char data){
//写内存流
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class CryptoFileStream : public FileStream
{
public:
virtual char Read(int number) {
//额外的加密操作...
FileStream::Read(number);
//额外的加密操作...
}
virtual void Seek(int position){
//额外的加密操作...
FileStream::Seek(position);
//额外的加密操作...
}
virtual void Write(char data){
//额外的加密操作...
FileStream::Write(data);
//额外的加密操作...
}
};

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){
//额外的加密操作...
NetworkStream::Write(data);
//额外的加密操作...
}
};

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){
//额外的加密操作...
MemoryStream::Write(data);
//额外的加密操作...
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BufferdFileStream : public FileStream {
//...
}
class BufferdNetworkStream : public NetworkStream {
//...
}
class BufferdMemoryStream : public MemoryStream {
//...
}


class CryptoBufferdFileStream : public BufferdFileStream {
//...
}
class CryptoBufferdNetworkStream : public BufferdNetworkStream {
//...
}
class CryptoBufferdMemoryStream : public BufferdMemoryStream {
//...
}
  • 类的继承关系及扩展组合方式如下:
    • 可以看到随着扩展种类增多,各种组合导致类的数量急剧增加
  • 使用代码如下:
    1
    2
    3
    4
    5
    //编译时装配
    FileStream * s1 = new FileStream();
    CryptoFileStream * s1 = new CryptoFileStream();
    BufferdFileStream * s2 = new BufferdFileStream();
    CryptoBufferdFileStream * s3 = new CryptoBufferdFileStream();

重构: 第一个版本,重要的思路转换

  • 扩展操作由继承改为组合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CryptoFileStream
{
FileStream * stream;
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
class CryptoNetworkStream
{
NetworkStream * stream;
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
class CryptoMemoryStream
{
MemoryStream * stream;
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
    class 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
    20
    class 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
    21
    class 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
    18
    class 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);
    }
    };

重构: 最后的版本,锦上添花

  • 可以发现第三个版本中CryptoStreamBufferdStream中都有成员指针,那么就可以尝试将其往底层提取

  • 一个直接的办法是放置在抽象类Stream中,但这样对三个主题类而言是多余的

  • 所以可以再封装一个类DecoratorStream

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class 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既缓冲又加密

总结

  • 根源分析:某些情况下”过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质(扩展类中调用子类的ReadSeekWrite),使得这种扩展方式缺乏灵活性。
  • 模式定义:动态(组合)的给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码&减少子类个数)