在软件设计中,如果责任划分不清晰,需求的变化时,子类会急剧膨胀,代码会大量重复。此时,划分责任是最关键的做法。
定义
动态的给一个对象增加职责,装饰模式比继承更加灵活。它能消除重复代码和减少子类个数。
实例
先看下面代码存在的问题。(子类膨胀,重复代码)
有文件流,网络流,加密流三种数据流,而每种数据流有加密,缓冲,加密且缓冲三种操作。
数据流的定义如下:
class Stream
{
public:
virtual char Read() = 0;
virtual void Write(char data) = 0;
virtual ~Stream(){}
};
//文件流
class FileStream :public Stream
{
public:
virtual char Read() override{};
virtual void Write(char data) override {};
virtual ~FileStream() {}
};
//网络流
class NetworkStream :public Stream
{
public:
virtual char Read() override {};
virtual void Write(char data) override {};
virtual ~NetworkStream() {}
};
//内存流
class MemoryStream :public Stream
{
public:
virtual char Read() override {};
virtual void Write(char data) override {};
virtual ~MemoryStream() {}
};
对数据流操作,定义如下:
//加密文件流
class CryptoFileStream :public FileStream
{
public:
virtual char Read() override
{
//加密操作
FileStream::Read();
};
virtual void Write(char data) override
{
//加密操作
FileStream::Write(data);
};
virtual ~CryptoFileStream()
{
}
};
//加密网络流
class CryptoNetworkStream :public NetworkStream
{
public:
virtual char Read() override
{
//加密操作
NetworkStream::Read();
};
virtual void Write(char data) override
{
//加密操作
NetworkStream::Write(data);
};
virtual ~CryptoNetworkStream()
{
}
};
//加密内存流
class CryptoMemoryStream :public MemoryStream
{
public:
virtual char Read() override
{
//加密操作
MemoryStream::Read();
};
virtual void Write(char data) override
{
//加密操作
MemoryStream::Write(data);
};
virtual ~CryptoMemoryStream()
{
}
};
//缓冲文件流
class BufferFileStream :public FileStream
{
public:
virtual char Read() override
{
//缓冲操作
FileStream::Read();
};
virtual void Write(char data) override
{
//缓冲操作
FileStream::Write(data);
};
virtual ~BufferFileStream()
{
}
};
//缓冲网络流
class BufferNetworkStream :public NetworkStream
{
public:
virtual char Read() override
{
//缓冲操作
NetworkStream::Read();
};
virtual void Write(char data) override
{
//缓冲操作
NetworkStream::Write(data);
};
virtual ~BufferNetworkStream()
{
}
};
//缓冲内存流
class BufferMemoryStream :public MemoryStream
{
public:
virtual char Read() override
{
//缓冲操作
MemoryStream::Read();
};
virtual void Write(char data) override
{
//缓冲操作
MemoryStream::Write(data);
};
virtual ~BufferMemoryStream()
{
}
};
.......
类的结构图如下:
上述代码中存在大量的重复代码,原因是将Stream的两个方向上的变化混在了一起,使用了继承而不是组合的方式组织代码。
下面使用组合的方式改进代码:
//加密流
class CryptoFileStream :public Stream
{
public:
virtual char Read() override
{
//加密操作
stream->Read();
};
virtual void Write(char data) override
{
//加密操作
stream->Write(data);
};
virtual ~CryptoFileStream()
{
}
private:
Stream *stream;
};
//缓冲文件流
class BufferFileStream :public Stream
{
public:
virtual char Read() override
{
//缓冲操作
stream->Read();
};
virtual void Write(char data) override
{
//缓冲操作
stream->Write(data);
};
virtual ~BufferFileStream()
{
}
private:
Stream *stream;
};
此时,代码已经简化很多,基本满足了需求。
用装饰模式继续优化上述代码:
//装饰器
class DecoratorStream:public Stream
{
protected:
Stream *stream;
};
//加密流
class CryptoFileStream :public DecoratorStream
{
public:
virtual char Read() override
{
//加密操作
stream->Read();
};
virtual void Write(char data) override
{
//加密操作
stream->Write(data);
};
virtual ~CryptoFileStream()
{
}
};
//缓冲文件流
class BufferFileStream :public DecoratorStream
{
public:
virtual char Read() override
{
//缓冲操作
stream->Read();
};
virtual void Write(char data) override
{
//缓冲操作
stream->Write(data);
};
virtual ~BufferFileStream()
{
}
};
类的结构图如下:
总结
装饰模式(Decorator)主要解决主体在多个方向上扩展的问题。
通过组合而不是继承的方式,装饰模式(Decorator)动态扩展对象的功能,同时避免了子类膨胀和代码重复的问题。