在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任,即遵循“单一职责”模式(属于此模式的有Decorator和Bridge)。
装饰模式(Decorator)
装饰模式可以解决的问题:
– 使“对象功能的扩展”能够根据需要来动态地实现
– 同时避免“扩展功能的增多”带来的子类膨胀问题
– 使得任何“功能扩展变化”所导致的影响降到最低
比如说现在需要去写流的操作,需要文件流、网络流、内存流,每种流都可能需要加密或者缓存,或者兼具加密和缓存。
下述写法是错误样例,当每种流都有可能有加密或缓存的扩展操作时,不应该将具有这些扩展操作的,都单独继承为一个类。当流的类型有n种,扩展操作有m种时,若按如下写法,则最多会写出 1+n+n*m!/2种子类。这样的代码重复度十分高。
可采取下述写法:
在这种写法里,DecoratorStream类(同时也继承自Stream)应该定义一个Stream的成员变量a,接受不同的Stream类型来构造,来初始化该成员,每个DecoratorStream子类应重写Stream的虚函数,在虚函数里调用a的该函数,并附加所需内容。
采用的组合的方式,
于此,所需流的实例创建代码为:
FileStream *s1 = new FileStream(); // 基本的文件流功能
CryptoStream *s2 = new CryptoStream(s1); // s2具有文件流加密功能
BufferedStream *s3 = new BufferedStream(s1); // s3具有文件流缓存功能
BufferedStream *s4 = new BufferedStream(s2); // s4具有文件流加密缓存功能
桥模式(Bridge)
由于某些类型固有的实现逻辑,使得它们具有多个变化的维度。若将其多个变化维度的内容糅杂在一个基类,则会使继承复杂。
错误示范:
class Messager{
public:
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual ~Messager() {}
};
// PC平台实现
class PCMessagerBase : public Messager{
public:
virtual void PlaySound(){
// ...
}
virtual void DrawShape(){
// ...
}
virtual void WriteText(){
// ...
}
virtual void Connect(){
// ...
}
};
// 业务抽象
// 1. 经典版的PC
class PCMessagerLite : public PCMessagerBase
{
public:
virtual void Login(string username, string password){
PCMessagerBase::Connect();
// .....
}
virtual void SendMessage(string message){
PCMessagerBase::WriteText();
// .....
}
virtual void SendPicture(Image image){
PCMessagerBase::DrawShape();
// .....
}
};
// 2. 华丽版的PC
class PCMessagerPefect : public PCMessagerBase
{
public:
virtual void Login(string username, string password){
// ..... new
PCMessagerBase::Connect();
// .....
}
virtual void SendMessage(string message){
// ..... new
PCMessagerBase::WriteText();
// .....
}
virtual void SendPicture(Image image){
// ..... new
PCMessagerBase::DrawShape();
// .....
}
};
// Mobile平台实现
class MobileMessagerBase : public Messager{
public:
virtual void PlaySound(){
// ...
}
virtual void DrawShape(){
// ...
}
virtual void WriteText(){
// ...
}
virtual void Connect(){
// ...
}
};
// 业务抽象
// 1. 经典版的Mobile
class MoblieMessagerLite : public MobileMessagerBase
{
public:
virtual void Login(string username, string password){
MobileMessagerBase::Connect();
// .....
}
virtual void SendMessage(string message){
MobileMessagerBase::WriteText();
// .....
}
virtual void SendPicture(Image image){
MobileMessagerBase::DrawShape();
// .....
}
};
// 2. 华丽版的Mobile
class MoblieMessagerPefect : public MobileMessagerBase
{
public:
virtual void Login(string username, string password){
// ..... new
MobileMessagerBase::Connect();
// .....
}
virtual void SendMessage(string message){
// ..... new
MobileMessagerBase::WriteText();
// .....
}
virtual void SendPicture(Image image){
// ..... new
MobileMessagerBase::DrawShape();
// .....
}
};
正确示例:
即Bridge模式解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。Bridge有时候类似于多继承方案,但多继承往往违背单一职责原则,复用性较差,Bridge是比多继承方案更好的解决方法。
class Messager{
protected:
PlatformFunc *plat;
public:
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
virtual ~Messager() {}
};
class PlatformFunc{
public:
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual ~PlatformFunc() {}
};
// Platform平台实现
class PCPlatform : public PlatformFunc{
public:
virtual void PlaySound(){
// ...
}
virtual void DrawShape(){
// ...
}
virtual void WriteText(){
// ...
}
virtual void Connect(){
// ...
}
};
// Mobile平台实现
class MobilePlatform : public PlatformFunc{
public:
virtual void PlaySound(){
// ...
}
virtual void DrawShape(){
// ...
}
virtual void WriteText(){
// ...
}
virtual void Connect(){
// ...
}
};
class MessagerLite : public Messager{
public:
virtual void Login(string username, string password){
plat->Connect();
// .....
}
virtual void SendMessage(string message){
plat->WriteText();
// .....
}
virtual void SendPicture(Image image){
plat->DrawShape();
// .....
}
};
class MessagerPerfect : public Messager{
public:
virtual void Login(string username, string password){
// ..... new
plat->Connect();
// .....
}
virtual void SendMessage(string message){
// ..... new
plat->WriteText();
// .....
}
virtual void SendPicture(Image image){
// ..... new
plat->DrawShape();
// .....
}
};