“单一职责”模式之装饰器模式

发布于:2025-07-01 ⋅ 阅读:(21) ⋅ 点赞:(0)

“单一职责”模式

  • 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
  • 典型目录
    • 装饰器模式 Decorator
    • 桥接模式 Bridge

装饰器模式 Decorator

引例

  1. 对于流操作,诸如文件流、网络流、内存流等,分别对其进行诸如加密、缓存等单个或组合的操作,子类庞大。
    // 业务操作
    class Stream
    {
    public:
    	virtual char Read(int number) = 0;
    	virtual char Seek(int position) = 0;
    	virtual char Write(char data) = 0;
    
    	virtual ~Stream(){}
    };
    
    // 主体类
    // 文件流
    class FileStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读文件流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位文件流
    	}
    	virtual char Write(char data)
    	{
    		// 写文件流
    	}
    };
    // 网络流
    class NetworkStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读网络流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 写网络流
    	}
    };
    // 内存流
    class MemoryStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读内存流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位内存流
    	}
    	virtual char Write(char data)
    	{
    		// 写内存流
    	}
    };
    
    
    //扩展操作,如加密、缓存、加密缓存
    // 加密文件流
    class CryptoFileStream : public FileStream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		FileStream::Read(number);	// 读文件流,这种为静态特质		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		FileStream::Seek(position);	// 定位文件流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		FileStream::write(data);// 写文件流
    	}
    };
    // 加密网络流
    class CryptoNetworkStream : public NetworkStream 
    {
    public:
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		NetworkStream ::Read(number);	// 读网络流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		NetworkStream ::Seek(position);	// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		NetworkStream ::write(data);// 写网络流
    	}
    };
    
    // 缓存文件流
    class BufferedFileStream : public FileStream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 额外的缓存操作
    		FileStream::Read(number);	// 读文件流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的缓存操作
    		FileStream::Seek(position);	// 定位文件流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的缓存操作
    		FileStream::write(data);// 写文件流
    	}
    };
    // 缓存网络流
    class BufferedNetworkStream : public NetworkStream 
    {
    public:
    	virtual char Read(int number)
    	{
    		// 额外的缓存操作
    		NetworkStream ::Read(number);	// 读网络流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的缓存操作
    		NetworkStream ::Seek(position);	// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的缓存操作
    		NetworkStream ::write(data);// 写网络流
    	}
    };
    
    
    // 加密缓存文件流
    class CryptoBufferedFileStream : public FileStream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		// 额外的缓存操作
    		FileStream::Read(number);	// 读文件流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		// 额外的缓存操作
    		FileStream::Seek(position);	// 定位文件流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		// 额外的缓存操作
    		FileStream::write(data);// 写文件流
    	}
    };
    // 加密网络流
    class CryptoBufferedNetworkStream : public NetworkStream 
    {
    public:
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		// 额外的缓存操作
    		NetworkStream ::Read(number);	// 读网络流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		// 额外的缓存操作
    		NetworkStream ::Seek(position);	// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		// 额外的缓存操作
    		NetworkStream ::write(data);// 写网络流
    	}
    };
    
    void Process()
    {
    	// 编译时装配
    	CryptoFileStream *cfs1 = new CryptoFileStream();
    	BufferedFileStream* bfs1 = new BufferedFileStream();
    	CryptoBufferedFileStream* cbfs1 = new CryptoBufferedFileStream();
    }
    

在这里插入图片描述
如第二层m种变化类型,第三层n种变化类型,类的个数 :1+ m + m * n!/ 2

  1. 对第一版代码进行重构

    // 业务操作
    class Stream
    {
    public:
    	virtual char Read(int number) = 0;
    	virtual char Seek(int position) = 0;
    	virtual char Write(char data) = 0;
    
    	virtual ~Stream(){}
    };
    
    // 主体类
    // 文件流
    class FileStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读文件流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位文件流
    	}
    	virtual char Write(char data)
    	{
    		// 写文件流
    	}
    };
    // 网络流
    class NetworkStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读网络流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 写网络流
    	}
    };
    // 内存流
    class MemoryStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读内存流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位内存流
    	}
    	virtual char Write(char data)
    	{
    		// 写内存流
    	}
    };
    
    
    //扩展操作,如加密、缓存、加密缓存
    // 加密文件流
    class CryptoStream : public Stream
    {
    	// FileStream* stream; 不必现在确定类型
    	Stream* stream;// = new FileStream(); 未来确实,即运行时确定
    public:
    	CryptoStream(Stream* st) : stream(st)
    	{
    	
    	}
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		stream->Read(number);	// 读流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		stream->Seek(position);	// 定位流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		stream->write(data);// 写流
    	}
    };
    // 加密网络流
    class CryptoNetworkStream : public Stream 
    {
    	// NetworkStream * stream; 不必现在确定类型
    	Stream* stream;// = new NetworkStream (); 未来确实,即运行时确定
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		stream->Read(number);	// 读网络流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		stream->Seek(position);	// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		stream->write(data);// 写网络流
    	}
    };
    // 加密文件流/网络流,合并为加密流CryptoStream,结构如下方缓存流
    
    // 缓存流
    class BufferedStream : public Stream
    {
    	Stream* stream;
    public:
    	BufferedStream(Stream* st) : stream(st)
    	{
    	}
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		stream->Read(number);	// 读流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		stream->Seek(position);	// 定位流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		stream->write(data);// 写流
    	}
    };
    
    void Process()
    {
    	// BufferedFileStream* bfs1 = new BufferedFileStream();	// 代码1
    	// CryptoBufferedFileStream* cbfs1 = new CryptoBufferedFileStream();	// 代码1
    	
    	// 运行时装配
    	FileStream* fs1 = new FileSteam();
    	CryptoStream* cfs1 = new CryptoStream(fs1);
    	BufferedStream* bfs1 = new BufferedStream(fs1);
    	CryptoStream* cbfs1 = new CryptoStream(bfs1);
    	
    }
    
  2. 对第二部分代码,继续重构,将共有部分加入到装饰类:DecoratorStream

    // 业务操作
    class Stream
    {
    public:
    	virtual char Read(int number) = 0;
    	virtual char Seek(int position) = 0;
    	virtual char Write(char data) = 0;
    
    	virtual ~Stream(){}
    };
    
    // 主体类
    // 文件流
    class FileStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读文件流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位文件流
    	}
    	virtual char Write(char data)
    	{
    		// 写文件流
    	}
    };
    // 网络流
    class NetworkStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读网络流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位网络流
    	}
    	virtual char Write(char data)
    	{
    		// 写网络流
    	}
    };
    // 内存流
    class MemoryStream : public Stream
    {
    public:
    	virtual char Read(int number)
    	{
    		// 读内存流
    	}
    	virtual char Seek(int position)
    	{
    		// 定位内存流
    	}
    	virtual char Write(char data)
    	{
    		// 写内存流
    	}
    };
    
    
    //扩展操作,如加密、缓存、加密缓存
    DecoratorStream : public Stream
    {
    protected:
    	Stream* stream;
    	DecoratorStream(Stream* st) : stream(st)
    	{
    	}
    }
    
    // 加密文件流
    class CryptoStream : public DecoratorStream
    {
    public:
    	CryptoStream(Stream* st) : DecoratorStream(st)
    	{
    	
    	}
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		stream->Read(number);	// 读流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		stream->Seek(position);	// 定位流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		stream->write(data);// 写流
    	}
    };
    
    // 缓存流
    class BufferedStream : public DecoratorStream
    {
    public:
    	BufferedStream(Stream* st) : stream(st)
    	{
    	}
    	virtual char Read(int number)
    	{
    		// 额外的加密操作
    		stream->Read(number);	// 读流		
    	}
    	virtual char Seek(int position)
    	{
    		// 额外的加密操作
    		stream->Seek(position);	// 定位流
    	}
    	virtual char Write(char data)
    	{
    		// 额外的加密操作
    		stream->write(data);// 写流
    	}
    };
    
    void Process()
    {
    	// 运行时装配
    	FileStream* fs1 = new FileSteam();
    	CryptoStream* cfs1 = new CryptoStream(fs1);
    	BufferedStream* bfs1 = new BufferedStream(fs1);
    	CryptoStream* cbfs1 = new CryptoStream(bfs1);
    	
    }
    

在这里插入图片描述
类的个数: 1 + m + 1 + n,使用组合(装饰器类中含有被修饰类的指针),来减少类的数量。

动机 Motivation

  • 在某些情况下,我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。

模式定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码及减少子类个数)。

结构 Structure

在这里插入图片描述
红色为稳定部分,蓝色为变化部分。

要点总结

  • 通过采用组合而非继承的手法,装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的“灵活性差”和“多子类衍生”问题。
  • Decorator 装饰器类在接口上表现为 is-a Component 的继承关系,即Decorator装饰器类继承了Component 所具有的接口;但在实现上又表现为 has-a Component的组合关系,即Decorator装饰器类又使用了另外一个Component类。
  • 装饰器模式的目的并非解决“多子类衍生的多继承”问题,其应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

来源:极客班——C++设计模式入门