c++ 设计模式6 (Decorator 装饰模式)
来源:互联网 发布:重大电气知乎 编辑:程序博客网 时间:2024/06/05 03:51
4. “单一职责”类模式
在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
典型模式代表: Decorator,Bridge
4.1 Decorator 装饰模式
代码示例:不同的流操作(文件流,网络流,内存流)及其扩展功能(加密,缓冲)等的实现
实现代码1:
类图结构示意(大量使用继承)
数据规模: 假设有n种文件,m种功能操作。该实现方法有(1 + n + n * m! / 2) 数量级的子类;
同时考察59行,79行,98行本身是相同的代码(类似还有很多),存在大量的冗余和重复。
开始重构,见方法2.
1 //Decorator1.cpp 2 //业务操作 3 class Stream{ 4 public: 5 virtual char Read(int number)=0; 6 virtual void Seek(int position)=0; 7 virtual void Write(char data)=0; 8 9 virtual ~Stream(){} 10 }; 11 12 //主体类 13 class FileStream: public Stream{ 14 public: 15 virtual char Read(int number){ 16 //读文件流 17 } 18 virtual void Seek(int position){ 19 //定位文件流 20 } 21 virtual void Write(char data){ 22 //写文件流 23 } 24 25 }; 26 27 class NetworkStream :public Stream{ 28 public: 29 virtual char Read(int number){ 30 //读网络流 31 } 32 virtual void Seek(int position){ 33 //定位网络流 34 } 35 virtual void Write(char data){ 36 //写网络流 37 } 38 39 }; 40 41 class MemoryStream :public Stream{ 42 public: 43 virtual char Read(int number){ 44 //读内存流 45 } 46 virtual void Seek(int position){ 47 //定位内存流 48 } 49 virtual void Write(char data){ 50 //写内存流 51 } 52 53 }; 54 55 //扩展操作 56 class CryptoFileStream :public FileStream{ 57 public: 58 virtual char Read(int number){ 59 60 //额外的加密操作... 61 FileStream::Read(number);//读文件流 62 63 } 64 virtual void Seek(int position){ 65 //额外的加密操作... 66 FileStream::Seek(position);//定位文件流 67 //额外的加密操作... 68 } 69 virtual void Write(byte data){ 70 //额外的加密操作... 71 FileStream::Write(data);//写文件流 72 //额外的加密操作... 73 } 74 }; 75 76 class CryptoNetworkStream : :public NetworkStream{ 77 public: 78 virtual char Read(int number){ 79 80 //额外的加密操作... 81 NetworkStream::Read(number);//读网络流 82 } 83 virtual void Seek(int position){ 84 //额外的加密操作... 85 NetworkStream::Seek(position);//定位网络流 86 //额外的加密操作... 87 } 88 virtual void Write(byte data){ 89 //额外的加密操作... 90 NetworkStream::Write(data);//写网络流 91 //额外的加密操作... 92 } 93 }; 94 95 class CryptoMemoryStream : public MemoryStream{ 96 public: 97 virtual char Read(int number){ 98 99 //额外的加密操作...100 MemoryStream::Read(number);//读内存流101 }102 virtual void Seek(int position){103 //额外的加密操作...104 MemoryStream::Seek(position);//定位内存流105 //额外的加密操作...106 }107 virtual void Write(byte data){108 //额外的加密操作...109 MemoryStream::Write(data);//写内存流110 //额外的加密操作...111 }112 };113 114 class BufferedFileStream : public FileStream{115 //...116 };117 118 class BufferedNetworkStream : public NetworkStream{119 //...120 };121 122 class BufferedMemoryStream : public MemoryStream{123 //...124 }125 126 127 128 129 class CryptoBufferedFileStream :public FileStream{130 public:131 virtual char Read(int number){132 133 //额外的加密操作...134 //额外的缓冲操作...135 FileStream::Read(number);//读文件流136 }137 virtual void Seek(int position){138 //额外的加密操作...139 //额外的缓冲操作...140 FileStream::Seek(position);//定位文件流141 //额外的加密操作...142 //额外的缓冲操作...143 }144 virtual void Write(byte data){145 //额外的加密操作...146 //额外的缓冲操作...147 FileStream::Write(data);//写文件流148 //额外的加密操作...149 //额外的缓冲操作...150 }151 };152 153 154 155 void Process(){156 157 //编译时装配158 CryptoFileStream *fs1 = new CryptoFileStream();159 160 BufferedFileStream *fs2 = new BufferedFileStream();161 162 CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();163 164 }
实现代码2:
针对上述代码,重构步骤如下:
1)考察 CryptoFileStream ,CryptoNetworkStream,CryptoMemoryStream三个类,将其继承FileStream,NetworkStream,NetworkStream改为组合;即
1 class CryptoFileStream{ 2 FileStream* stream; 3 public: 4 virtual char Read(int number){ 5 6 //额外的加密操作... 7 stream -> Read(number);//改用字段方式调用Read() 8 // ...seek() write() 同理 9 }10 } 11 12 class CryptoNetworkStream{13 NetworkStream* stream;14 public:15 virtual char Read(int number){16 17 //额外的加密操作...18 stream -> Read(number);//改用字段方式调用Read()19 //... seek() write() 同理20 }21 } 22 23 class CryptoMemoryStream{24 MemoryStream* stream;25 public:26 virtual char Read(int number){27 28 //额外的加密操作...29 stream -> Read(number);//改用字段方式调用Read()30 //... seek() write() 同理31 }32 }
2)考察上述2行, 13行, 24行, 发现其均为Stream子类, 应使用多态性继续重构。
1 class CryptoFileStream{ 2 Stream* stream; // = new FileStream() 3 public: 4 virtual char Read(int number){ 5 6 //额外的加密操作... 7 stream -> Read(number);//改用字段方式调用Read() 8 // ...seek() write() 同理 9 }10 } 11 12 class CryptoNetworkStream{13 Stream* stream; // = new NetworkStream();14 public:15 virtual char Read(int number){16 17 //额外的加密操作...18 stream -> Read(number);//改用字段方式调用Read()19 //... seek() write() 同理20 }21 } 22 23 class CryptoMemoryStream{24 Stream* stream; // = newMemoryStream()25 public:26 virtual char Read(int number){27 28 //额外的加密操作...29 stream -> Read(number);//改用字段方式调用Read()30 //... seek() write() 同理31 }32 }
3)发现三个类是相同的,不同的实现(需求的变化)是在运行时实现,编译时复用,改为一个类即可,命名为CryptoStream。
同时为了保证接口规范(read,seek等仍然是虚函数),继承Stream,出现既有组合,又有继承的情况。
1 class CryptoStream : public Stream{ 2 Stream* stream; // = new ... 3 public: 4 virtual char Read(int number){ 5 6 //额外的加密操作... 7 stream -> Read(number);//改用字段方式调用Read() 8 // ...seek() write() 同理 9 }10 }
4)添加相应构造器,得到此轮重构后的结果,代码如下,主要查看使用方式(运行时装配):
1 //Decorator2.cpp 2 class Stream{ 3 4 public: 5 virtual char Read(int number)=0; 6 virtual void Seek(int position)=0; 7 virtual void Write(char data)=0; 8 9 virtual ~Stream(){} 10 }; 11 12 //主体类 13 class FileStream: public Stream{ 14 public: 15 virtual char Read(int number){ 16 //读文件流 17 } 18 virtual void Seek(int position){ 19 //定位文件流 20 } 21 virtual void Write(char data){ 22 //写文件流 23 } 24 25 }; 26 27 class NetworkStream :public Stream{ 28 public: 29 virtual char Read(int number){ 30 //读网络流 31 } 32 virtual void Seek(int position){ 33 //定位网络流 34 } 35 virtual void Write(char data){ 36 //写网络流 37 } 38 39 }; 40 41 class MemoryStream :public Stream{ 42 public: 43 virtual char Read(int number){ 44 //读内存流 45 } 46 virtual void Seek(int position){ 47 //定位内存流 48 } 49 virtual void Write(char data){ 50 //写内存流 51 } 52 53 }; 54 55 //扩展操作 56 57 58 class CryptoStream: public Stream { 59 60 Stream* stream;//... 61 62 public: 63 CryptoStream(Stream* stm):stream(stm){ 64 65 } 66 67 68 virtual char Read(int number){ 69 70 //额外的加密操作... 71 stream->Read(number);//读文件流 72 } 73 virtual void Seek(int position){ 74 //额外的加密操作... 75 stream::Seek(position);//定位文件流 76 //额外的加密操作... 77 } 78 virtual void Write(byte data){ 79 //额外的加密操作... 80 stream::Write(data);//写文件流 81 //额外的加密操作... 82 } 83 }; 84 85 86 87 class BufferedStream : public Stream{ 88 89 Stream* stream;//... 90 91 public: 92 BufferedStream(Stream* stm):stream(stm){ 93 94 } 95 //... 96 }; 97 98 99 100 101 102 void Process(){103 104 //运行时装配105 FileStream* s1=new FileStream();106 CryptoStream* s2=new CryptoStream(s1);107 108 BufferedStream* s3=new BufferedStream(s1);109 110 BufferedStream* s4=new BufferedStream(s2);111 112 113 114 }
实现代码3:
上述实现代码2已经极大地缓解了冗余问题,符合面向对象的设计思想,该轮重构是锦上添花。
重构步骤如下:
考察上述代码,多个子类都有同样的字段(Stream* stream;//...)
应考虑“往上提”,方法有两种,第一种是提到基类(显然不合适,FileStream等并不需要Stream字段 )
所以考虑第二种方法,实现一个“中间类”。
DecoratorStream: public Stream{protected: Stream* stream;//... DecoratorStream(Stream * stm):stream(stm){ } };
CryptoStream等继承中间类DecoratorStream:
class CryptoStream: public DecoratorStream { public: CryptoStream(Stream* stm):DecoratorStream(stm){ } //...}
重构完成的最终版本:
FileStream,NetworkStream,MemoryStream等可以创建各自的对象;
但实现加密,缓存功能必须在已有FileStream/NetworkStream等对象基础上;
这些操作本质是扩展操作,也就是“装饰”的含义。
此时类图示意:
这时类的数量为(1 + n + 1 + m)
1 //Decorator3.cpp 2 class Stream{ 3 4 public: 5 virtual char Read(int number)=0; 6 virtual void Seek(int position)=0; 7 virtual void Write(char data)=0; 8 9 virtual ~Stream(){} 10 }; 11 12 //主体类 13 class FileStream: public Stream{ 14 public: 15 virtual char Read(int number){ 16 //读文件流 17 } 18 virtual void Seek(int position){ 19 //定位文件流 20 } 21 virtual void Write(char data){ 22 //写文件流 23 } 24 25 }; 26 27 class NetworkStream :public Stream{ 28 public: 29 virtual char Read(int number){ 30 //读网络流 31 } 32 virtual void Seek(int position){ 33 //定位网络流 34 } 35 virtual void Write(char data){ 36 //写网络流 37 } 38 39 }; 40 41 class MemoryStream :public Stream{ 42 public: 43 virtual char Read(int number){ 44 //读内存流 45 } 46 virtual void Seek(int position){ 47 //定位内存流 48 } 49 virtual void Write(char data){ 50 //写内存流 51 } 52 53 }; 54 55 //扩展操作 56 57 DecoratorStream: public Stream{ 58 protected: 59 Stream* stream;//... 60 61 DecoratorStream(Stream * stm):stream(stm){ 62 63 } 64 65 }; 66 67 class CryptoStream: public DecoratorStream { 68 69 70 public: 71 CryptoStream(Stream* stm):DecoratorStream(stm){ 72 73 } 74 75 76 virtual char Read(int number){ 77 78 //额外的加密操作... 79 stream->Read(number);//读文件流 80 } 81 virtual void Seek(int position){ 82 //额外的加密操作... 83 stream::Seek(position);//定位文件流 84 //额外的加密操作... 85 } 86 virtual void Write(byte data){ 87 //额外的加密操作... 88 stream::Write(data);//写文件流 89 //额外的加密操作... 90 } 91 }; 92 93 94 95 class BufferedStream : public DecoratorStream{ 96 97 Stream* stream;//... 98 99 public:100 BufferedStream(Stream* stm):DecoratorStream(stm){101 102 }103 //...104 };105 106 107 108 109 void Process(){110 111 //运行时装配112 FileStream* s1=new FileStream();113 114 CryptoStream* s2=new CryptoStream(s1);115 116 BufferedStream* s3=new BufferedStream(s1);117 118 BufferedStream* s4=new BufferedStream(s2);119 120 121 122 }
Decorator模式使用动机:
在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于基础为类型引入的静态特指,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各个子类的组合(扩展功能的组合)会导致各种子类的膨胀。
模式定义:
动态(组合)地给一个对象增加一些额外的指责。就增加功能而言,Decorator模式比声场子类(继承)更为灵活(消除重复代码&减少子类个数)
类图:
要点总结:
1.通过采用组合并非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的”灵活性差“和”多子类衍生问题“
2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
3.Decorator模式的目的并非解决”多字类衍生的多继承“问题,Decorator模式应用的要点在于解决”主体类在多个方向上的扩展功能“(显然file,network与加密,缓冲是两种扩展方向) ——是为”装饰“的含义。
参考文献:
李建忠老师 《C++设计模式》网络课程
《设计模式:可复用面向对象软件的基础》
- (C++)设计模式------装饰者模式 decorator
- 设计模式 - 装饰 Decorator
- Decorator(装饰)设计模式
- 设计模式[6] Decorator Pattern 装饰模式
- 设计模式-装饰模式(Decorator Pattern)
- 设计模式---装饰模式(decorator)
- 设计模式 (十)装饰模式(Decorator)
- 设计模式 (十)装饰模式(Decorator)
- 设计模式 (十)装饰模式(Decorator)
- 【设计模式】之装饰模式(Decorator)
- 设计模式之装饰模式(Decorator)
- 设计模式之 装饰模式(Decorator)
- 设计模式之装饰模式(Decorator)
- Java设计模式---装饰模式(Decorator)
- 设计模式(五):装饰模式(Decorator)
- 设计模式(7)--Decorator 装饰模式
- 设计模式:装饰模式(Decorator)
- 设计模式之装饰(Decorator)模式
- win10将有毛玻璃
- c++ 设计模式8 (Factory Method 工厂方法)
- c++ 设计模式7 (Bridge 桥模式)
- 利用CVX工具箱实现单快拍的稀疏矩阵DOA估计
- CG中的几何学——坐标系【2】
- c++ 设计模式6 (Decorator 装饰模式)
- android 解析json
- Android布局的基本概念及布局的运用?
- 51NOD 1434 区间LCM
- C语言再学习 -- 常用头文件和函数
- 《正则表达式》
- c++ 设计模式5 (Observer / Event 观察者模式)
- 《挑战程序设计竞赛》阅读笔记二 之 ALDS1_2_B Selection Sort
- 文章标题