1. 动机
GOF《设计模式---可复用面向对象软件基础》这样描述:将它的抽象部分和实现部分分离,使得它们独立变化,互不影响。
2. 解决的问题
当一个抽象有很多实现时,通常需要继承来协调。抽象类定义相关的接口,其具体的子类对接口进行重写,以此完成不同的实现。但继承机制使得抽象部分和实现部分固定在一起(修改父类,子类将受影响),难以对抽象部分和实现部分进行独立的修改、扩充和重用。为解决继承机制带来的紧耦合问题,桥接模式将抽象和实现分离,抽象不依赖于实现,让抽象和实现部分各自修改起来都很方便,并使用了组合(Abstraction中包含了Implementor)的方式,降低了抽象和实现的耦合度(继承机制紧耦合),使得类容易修改、扩充和重用。请记住:尽量使用桥接模式而非继承机制。
3. 模式中的角色
- Abstraction(抽象):定义了抽象类的接口,并维护一个指向Implementor类型对象的指针。简单粗暴的来讲:Abstraction这个抽象类中声明了Implementor(实现)的类对象,通过该类对象可以访问到Implementor中的方法或属性。
- RefinedAbstaction(提炼的抽象):继承自Abstraction,并扩充(就是定义)Abstraction中的接口。
- Implementor(实习):定义实现类的接口,在C++中是纯虚函数,父类定义接口,子类实现接口。
- ContreteImplementor(具体实现):抽象基类Implementor的子类,实现父类中定义的接口。
4. 桥接模式类图
抽象类和实现类如何协作:在桥接模式中,抽象类中维护的指向实现类类型的对象指针起到桥梁作用,用于抽象类和实现类的沟通。简单来说:Abstraction将client的请求转发给它的Implementor对象。
5. C++代码实现
#include <iostream>
// ---------------- 实现 -------------------------
class Implementor{
public:
virtual void OperationImpl() = 0;
};
class ConcreteImplementor : public Implementor{
public:
virtual void OperationImpl(){
std::cout << "OperationImpl in ConcreteImplementor" << std::endl;
}
};
// ---------------- 抽象 -------------------------
class Abstraction{
public:
Abstraction(Implementor *implemetor) :_implemetor(implemetor){}
virtual void Operation() = 0;
protected:
Implementor *_implemetor;
};
class RefinedAbstraction : public Abstraction{
public:
RefinedAbstraction(Implementor *implemetor) : Abstraction(implemetor){}
virtual void Operation(){
std::cout << "Operation in RefinedAbstraction" << std::endl;
_implemetor->OperationImpl();
}
};
void main(){
Implementor *impl = new ConcreteImplementor(); // new Implementor();// ERROR!不允许使用抽象类型的Implementor(Implementor::OpertionImpl()为纯虚函数)对象
Abstraction *refineAbstraction = new RefinedAbstraction(impl);
refineAbstraction->Operation();
delete refineAbstraction;
refineAbstraction = NULL;
delete impl;
impl = NULL;
std::cin.get();
}
6. 何时使用桥接模式?
- 不希望在抽象和它的实现部分之间有一个固定的绑定关系,也就是继承关系;如果我们打破了这种固定的绑定关系,以后,就可以方便的在抽象部分切换不同的实现部分;
- 如果希望类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充;
- 如果不使用桥接模式,使用继承去实现时,在抽象类中添加一个方法,则在对应的实现类中也需要做对应的改动,这种实现不符合松耦合的要求;
- 如果要求对一个抽象的实现部分的修改对客户不产生影响,即客户的代码不需要重新编译,在后面的项目经验会说这方面;
- 如果想对客户完全隐藏抽象的实现部分;
- 如果一个对象有多个变化因素的时候,通过抽象这些变化因素,将依赖具体实现,修改为依赖抽象;
- 如果某个变化因素在多个对象中共享时,可以抽象出这个变化因素,然后实现这些不同的变化因素。
注:参考果冻博文C++设计模式——桥接模式
7. 桥接模式学习资料
有两篇博客写的很不错,其中一篇是老聚的设计模式学习笔记-桥接模式,另外一篇是果冻想C++设计模式——桥接模式;除此之外,程杰的《大话设计模式》和GOF的《设计模式---可复用面向对象软件基础》中也有详细的解释。