目录
前言
在学习侯捷老师的有关设计模式的课程(李建忠老师主讲)中,老师对23种设计模式的有自己的划分,如下。所以老师讲解是按照这种顺序讲解。
单一职责:
Bridge设计模式
1.模式动机
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。
所以Bridge模式的动机就是应对“多维度的变化
”。
看文字描述表枯燥,下面举几个例子:
- 商城系统中的电脑分类:如
电脑类型+电脑品牌
,这是两个维度的变化。
图形形状+颜色填充
两种变化的维度
下面是桥模式的实际看法场景:
参考至:参考
下面给一个具体的案例:
2.示例1
先看代码,后面解释会清楚点。
#include <iostream>
using namespace std;
//实现抽象
class MessagerImp{
public:
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual ~MessagerImp(){}
};
//平台实现
class PCMessagerImp : public MessagerImp{//PC平台
public:
virtual void PlaySound(){
//**********
cout << "PC::PlaySound ,播放声音." << endl;
}
virtual void DrawShape(){
//**********
cout << "PC::DrawShape ,画图形." << endl;
}
virtual void WriteText(){
//**********
cout << "PC::WriteText ,写文本." << endl;
}
virtual void Connect(){
//**********
cout << "PC::Connect ,登录连接." << endl;
}
};
class MobileMessagerImp : public MessagerImp{//PC平台
public:
virtual void PlaySound(){
//**********
cout << "Mobile::PlaySound ,播放声音." << endl;
}
virtual void DrawShape(){
//**********
cout << "Mobile::DrawShape ,画图形." << endl;
}
virtual void WriteText(){
//**********
cout << "Mobile::WriteText ,写文本." << endl;
}
virtual void Connect(){
//**********
cout << "Mobile::Connect ,登录连接." << endl;
}
};
//业务功能抽象
class Messager{
protected:
MessagerImp* messagerImp;//拿着实现(MessagerImp)的基类指针
Messager(MessagerImp* _messagerImp) : messagerImp(_messagerImp){}
public:
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture() = 0;
virtual ~Messager(){}
};
//业务实现
class MessagerLite :public Messager {//Lite
public:
MessagerLite(MessagerImp* _messagerImp) : Messager(_messagerImp){}
virtual void Login(string username, string password){
cout << "Lite::Login , 登录..." << endl;
messagerImp->Connect();
//........
}
virtual void SendMessage(string message){
cout << "Lite::SendMessage , 发送登录消息..." << endl;
messagerImp->WriteText();
//........
}
virtual void SendPicture(){
cout << "Lite::SendMessage , 发送图片..." << endl;
messagerImp->DrawShape();
//........
}
};
class MessagerPerfect :public Messager {
public:
MessagerPerfect(MessagerImp* _messagerImp) : Messager(_messagerImp){}
virtual void Login(string username, string password){
cout << "Perfect::Login , 登录..." << endl;
messagerImp->PlaySound();
//********
messagerImp->Connect();
//........
}
virtual void SendMessage(string message){
cout << "Perfect::SendMessage , 发送登录消息..." << endl;
messagerImp->PlaySound();
//********
messagerImp->WriteText();
//........
}
virtual void SendPicture(){
cout << "Perfect::SendMessage , 发送图片..." << endl;
messagerImp->PlaySound();
//********
messagerImp->DrawShape();
//........
}
};
int main()
{
Messager* mes = new MessagerLite(new PCMessagerImp);//生成精简版(Lite)Message,在PC平台下
mes->Login("xxx","123");
mes->SendMessage("success");
mes->SendPicture();
cout << "-------------------------------" << endl;
Messager* mes1 = new MessagerPerfect(new PCMessagerImp);//生成完美版(Perfector)Message,在PC平台下
mes1->Login("xxx", "123");
mes1->SendMessage("success");
mes1->SendPicture();
system("pause");
return 0;
}
有一个Message
类,有Login、SendMessage、SendPicture
3种抽象方法,精简版(Lite)和完美版(Perfector)会对这3种抽象方法进行相应的覆写(这里为了方便,只会对Perfector
版加入一个播放声音的操作)。
又在不同的平台(PC、Mobile
)下,部分操作PlaySound、DrawShape、WriteText、Connect
不相同(这里的操作定是和Message类相关,不然就可以把二者独立开,和Bridge模式便毫无关系)。如下图所示,就有两个不同的维度
。
前面已经说过,桥模式就是应对这样多维度的变化
。具体做法就是:
我们通过让Message
得到MessageImp
的指针(多态),这样,客户程序就可以组合Lite、Perfector
和PC、Mobile
。并且,可以很好的应对未来的扩展,比如MessageImp
可以扩展一个Mac
。
注: Message
和MessageImp
均是接口。
另外,你在设计时,也许可能会做如下设计:
首先Message
类,将业务实现方法:Login、SendMessage、SendPicture
和不同平台下的方法:PlaySound、DrawShape、WriteText、Connect
均放在这里。如下
class Messager{
public:
virtual void Login(string username, string password)=0;
virtual void SendMessage(string message)=0;
virtual void SendPicture()=0;
virtual void PlaySound()=0;
virtual void DrawShape()=0;
virtual void WriteText()=0;
virtual void Connect()=0;
virtual ~Messager(){}
};
然后通过继承的方式来做:
让MobileMessage、PCMessage
覆写不同平台下的方法:PlaySound、DrawShape、WriteText、Connect
。
接着在MobilePerfector、MobileLite、PCPerfector、PCPerfector
分别覆写业务Login、SendMessage、SendPicture
。
示例图如下:
假设现在我加一个Mac
平台,那么就要增加3个类,如果是10个类呢,就是10*3 = 30个类,而用桥模式只要10个即可。可以想象这种方法的可扩展性有多差。
3.示例2:图形和颜色
下面实现在模式动机中给的例子:图形形状和颜色填充
。
直接给代码:
#include <iostream>
using namespace std;
//实现抽象
class Color
{
public:
virtual void putColor() = 0;
virtual ~Color(){}
};
class RedColor : public Color
{
public:
virtual void putColor()
{
//...
cout << "上红色" << endl;
//...
}
};
class BullColor : public Color
{
public:
virtual void putColor()
{
//...
cout << "上蓝色" << endl;
//...
}
};
class YellowColor : public Color
{
public:
virtual void putColor()
{
//...
cout << "上黄色" << endl;
//...
}
};
class Shape
{
protected:
Color* color;
Shape(Color* _color) :color(_color){}
public:
virtual void drawShape() = 0;
virtual ~Shape(){};
};
class Rectangle : public Shape
{
public:
Rectangle(Color* _color) :Shape(_color){}
virtual void drawShape()
{
cout << "矩形" << "->";
color->putColor();
}
};
class Circle : public Shape
{
public:
Circle(Color* _color) :Shape(_color){}
virtual void drawShape()
{
cout << "圆形" << "->";
color->putColor();
}
};
class Triangle : public Shape
{
public:
Triangle(Color* _color) :Shape(_color){}
virtual void drawShape()
{
cout << "三角形" << "->";
color->putColor();
}
};
int main()
{
Shape* sh = new Rectangle(new BullColor);
sh->drawShape();
sh = new Circle(new YellowColor);
sh->drawShape();
system("pause");
return 0;
}
4.模式定义
注: 什么叫将抽象和实现分离呢?
- 这里的抽象指的并
不是抽象类或者接口
,而是被抽象出来的一套“类库”,它只包含骨架代码,真正的业务逻辑需要委托给定义中的“实现”来完成
。 - 我们这里所说的实现也绝非接口的实现类,而
是一套独立的“类库”
。 - “抽象”和“实现”独立开发,通过对象之间的组合关系,组装在一起。
上述描述参考:
https://blog.csdn.net/qq_35423154/article/details/111085839
5.模式结构
Abstraction
就像示例中的Message
和Shape
。
Implementor
就像示例中的MessageImp
和Color
。
6.要点总结
-
Bridge模式使用“对象间的组合关系”解耦了抽象和实现(抽象出骨架代码,在骨架代码中某些步骤在实现中完成)之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
-
Bridge模式有时候类似于多继承方案(就像在示例2中,生成一个红色矩形,可以看成是同时继承
Shape
类和Color
类),但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法
。 -
Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式(桥模式就是解决多维度的问题)。