桥模式(Bridge)
动机(Motivation)
由于某些类型的固有实现逻辑,使得他们具有两个变化的维度,乃至多个维度的变化
如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松沿着两个乃至多个方向变化,而不引入额外复杂度?
模式定义
将抽象部分(业务功能)与实现部分(平台实现)分离,使他们都可以独立的变化。 ——《设计模式》GoF
结构(Struture)
(1)Abstraction(抽象类)
用于定义抽象类的接口,其中定义了一个Implementation(实现类接口)的对象并可以维护该对象,它与 Implementation具有关联关系。
(2)Refined Abstraction(扩充抽象类)
扩充由Abstraction定义的接口,通常它不再是抽象类而是具体类。提供控制逻辑的变体。与其父类一样,它们通过通用实现接口与不同的实现进行交互
提供高层控制逻辑,依赖于完成底层实际工作的实现对像
(3)Implementation(实现类接口)
为所有具体实现声明通用接口。抽象部分仅能通过在这里声明的方法与实现对象交互
(4)ConcreateImplementations(具体实现类)
包括特定的实现代码
(5)Client(客户端)
仅关心如何与抽象部分合作。但是,Client需要将抽象对象与一个实现对象连接起来。
实例
如手机支付方式微信、支付宝APP,而每种APP支持三种付款验证方式,密码、指纹、人脸,则桥接模式的实现方式如下:
IPayMode类是实现类,定义了基本操作security(),用于设置验证方式,其子类提供了实现,它位于桥接模式的抽象层。
#include <iostream>
using namespace std;
// 实现类接口 Implementation(实现类接口):支付模式
class IPayMode {
public:
virtual bool security(string Uid) = 0;
};
// 具体实现类:密码支付
class PayCypher : public IPayMode {
public:
bool security(string uId) override {
cout << "Password to pay" << endl;
return true;
}
};
// 具体实现类:人脸支付
class PayFaceMode : public IPayMode {
public:
bool security(string uId) override {
cout << "Face to pay" << endl;
return true;
}
};
// 具体实现类:指纹支付
class PayFingerprintMode : public IPayMode {
public:
bool security(string uId) override {
cout << "fingo-pay" << endl;
return true;
}
};
// 抽象化类:支付
class Pay {
public:
Pay(IPayMode* payMode) {
this->payMode = payMode;//抽象类中定义了一个 IPayMode,与IPayMode存在关联关系
}
virtual ~Pay() { delete payMode; }
virtual string transfer(string uId, string tradeId, long long amount) = 0;
IPayMode* payMode;
};
// 扩展抽象化角色: 微信支付
class WxPay : public Pay {
public:
WxPay(IPayMode* payMode) : Pay(payMode) { }
//扩展抽象类 实现了抽象类中定义的方法
string transfer(string uId, string tradeId, long long amount) {
cout << "WeChat pay start, uId: " << uId << " tradeId: "
<< tradeId << "amount: " << amount << endl;
bool security = payMode->security(uId);
if (!security) {
cout << "WeChat pay failed, uId: " << uId << " tradeId: "
<< tradeId << " amount: " << amount << endl;
return "0001";
}
cout << "WeChat pay succeed, uId: " << uId << " tradeId: "
<< tradeId << " amount: " << amount << endl;
return "0000";
}
};
// 扩展抽象化角色:支付宝支付
class ZfbPay : public Pay {
public:
ZfbPay(IPayMode* payMode) : Pay(payMode) { }
string transfer(string uId, string tradeId, long long amount) {
cout << "Alipay pay start, uId: " << uId << " tradeId: "
<< tradeId << " amount: " << amount << endl;
bool security = payMode->security(uId);
if (!security) {
cout <<"Alipay pay failed,uId: " << uId << " tradeId: "
<< tradeId << " amount: " << amount << endl;
return "0001";
}
cout <<"Alipay pay succeed,uId: " << uId << " tradeId: "
<< tradeId << " amount: " << amount << endl;
return "0000";
}
};
// 客户端使用
int main() {
cout << "--------Please select the method of payment------------" << endl;
Pay* wxPay = new WxPay(new PayFaceMode());
wxPay->transfer("weixin_666", "a123456", 290);
cout << endl;
cout << "--------Please select the method of payment----------" << endl;
Pay* zfbPay = new ZfbPay(new PayCypher());
zfbPay->transfer("zfb_888, "a123456", 319);
}
优缺点
优点:
(1)分离抽象类及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间 固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
(2)桥接模式是比多继承方案更好的解决方法。
(3)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统。
(4)实现细节对客户透明,可以对用户隐藏实现细节。用户在使用时不需要关系实现,在抽象层通过聚合关联关系完成封装与对象组合。
缺点:
(1)桥接模式的引入会增加系统的理解与设计难度。聚合关系建立在抽象层,要求开发者针对抽象进行设计与编程。
(2)桥接模式要求正确识别出系统的两个独立变化的维度,因此使用范围具有一定的局限性。
要点总结
-
Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
-
Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单,复用性比较差。职责原则(即一个类只有一个变化的原因)Bridge模式是比多继承方案更好的解决方法
-
Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。