桥接模式
1、背景
在生活中,手机的品牌多种多样,手机的软件功能同样五花八门,现在要求不同的品牌手机具有类似的功能,比如通讯录和游戏,如何设计。
1、根据不同的品牌划分,可以得到如下UML图
:
这样划分存在问题,如果我还需要增加‘输入法’功能、‘拍照’功能,再增加‘L品牌’、‘X品牌’
你的类如何写?”继承关系变得原来越复杂。
2、根据不同的手机功能划分,可以得到如下UML图
:
同样,要是增加手机品牌或者手机功能,也会产生很大影响。
我们一直在用面向对象的理论设计的,先有一个品牌,然后多个品牌就抽象出一个品牌抽象类,对于每个功能,就都继承各自的品牌。或者,不从品牌,从手机软件的角度去分类,同样问题多多。继承结构,如果不断地增加新品牌或新功能,类会越来越多的。
对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。
3、基于合成复用原则
设计方案,可以得到如下UML图
:
- 实际上,像‘游戏’、‘通讯录’、“MP3音乐播放’这些功能都是软件,如果我们可以让其分离与手机的耦合,那么就可以大大减少面对新需求时改动过大的不合理情况。
- 所以应该有个‘手机品牌’抽象类和‘手机软件’抽象类,让不同的品牌和功能都分别继承于它们,这样要增加新的品牌或新的功能都不用影响其他类了。
优先使用对象的组合/聚合
将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
上面所要说明的就是合成复用原则(Composite Reuse Principle, CRP),由此引出咱们的桥接模式。
2、定义
1、定义
将抽象部分与它的实现部分分离,使他们都可以独立变化。
-
“这里需要理解一下,什么叫抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象。
- ‘手机’既可以按照品牌来分类,也可以按照功能来分类。”
-
桥接模式所说的‘将抽象部分与它的实现部分分离’,就是实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合
2、结构说明
- 1、抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
- 2、扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 3、实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
- 4、具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
3、特征
1.桥接模式的优点
-
(1)实现了抽象和实现部分的分离
抽象部分和实现部分独立开来,分别定义接口,这有助于系统进行分层设计,从而产生更好的结构化系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。 -
(2)更好的可扩展性
由于桥接模式把抽象部分和实现部分分离了,从而分别定义接口,这就使得抽象部分和实现部分可以分别独立扩展,而不会相互影响 -
(3)可动态的切换实现
由于桥接模式实现了抽象和实现的分离,所以在实现桥接模式时,就可以实现动态的选择和使用具体的实现。 -
(4)实现细节对客户端透明,可以对用户隐藏实现细节。
2.桥接模式的缺点
- (1)桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
- (2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性。
3.桥接模式的使用场景
- (1)如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
- (2)抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
- (3)一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
- (4)虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
- (5)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
4、应用
#include <iostream>
using namespace std;
//软件抽象类
class Software
{
public:
virtual void run() = 0;
};
//软件具体类
class SoftwareGame : public Software
{
public:
void run()
{
cout << "王者荣耀" << endl;
}
};
//软件具体类
class SoftwareNote : public Software
{
public:
void run()
{
cout << "记事本软件" << endl;
}
};
//手机抽象类
class Phone
{
protected:
Software * m_software;
public:
Phone() : m_software(nullptr) {}
virtual ~Phone()
{
if (nullptr == m_software)
delete m_software;
}
virtual void run() = 0;
void setSoftware(Software *p)
{
m_software = p;
}
};
//手机具体类
class Huawei : public Phone
{
public:
void run()
{
cout << "Huawei P40 running " ;
m_software->run();
}
};
class Oppo : public Phone
{
public:
void run()
{
cout << "OPPO Find running " ;
m_software->run();
}
};
int main()
{
Phone *p = new Huawei();
p->setSoftware(new SoftwareGame());
p->run();
p = new Oppo();
p->setSoftware(new SoftwareNote());
p->run();
delete p;
return 0;
}
参考
1、https://www.cnblogs.com/lixiuyu/p/5923160.html
2、https://blog.csdn.net/konglongdanfo1/article/details/83381476
3、《大话设计模式》