设计模式——桥接模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式——桥接模式相关的知识,希望对你有一定的参考价值。

设计模式(十一)——桥接模式

一、桥接模式简介

1、桥接模式简介

    桥接模式将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化。

    桥接模式将抽象和实现分别独立实现,Abstraction类和Implement类。

    桥接模式中的实现不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是怎么去实现用户的需求,即在Implement具体类中实现Abstraction的接口功能,并且是通过组合(委托)的方式实现的,因此桥接模式实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。

技术分享

    桥接模式将继承关系转换为组合关系,从而降低了系统间的耦合,减少了代码编写量。使用组合(委托)的方式将抽象和实现彻底地解耦,好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。

    将抽象部分与它的实现部分分离,使得它们可以独立地变化抽象Abstraction与实现Implement分离抽象部分Abstraction可以变化,如new RefinedAbstractionA(imp)new RefinedAbstractionB(imp)实现部分Implement也可以独立变化,如new ConcreteImplementA()new ConcreteImplementB()

2、桥接模式角色

    抽象类(Abstraction):定义抽象类接口并且维护一个指向Implement的指针

    扩充抽象类(RefinedAbstraction)扩充由Abstraction定义的接口

    实现类接口(Implement)定义实现类的接口,该接口不一定要与 Abstraction的接口完全一致;事实上这两个接口可以完全不同。一般来讲, Implement接口仅提供基本操作,而 Abstraction则定义了基于这些基本操作的较高层次的操作。

具体实现类(ConcreteImplement)实现Implement接口并定义具体实现。

 Abstraction::request():定义要实现的操作接口,在Abstraction::request()中根据不同的指针多态调用Implement::operation()函数。

        Implement::operation():实现抽象类Abstaction所定义操作的接口,由其具体派生类ConcreteImplemenAConcreteImplemenB或者其他派生类实现。

3、桥接模式优缺点

    优点:

    A分离接口及其实现部分 一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。将AbstractionImplement分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。另外,接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道AbstractionImplement即可。
    B提高可扩充性 可以独立地对AbstractionImplement层次结构进行扩充。

 C实现细节对客户透明 可以对客户隐藏实现细节,例如共享Implement对象以及相应的引用计数机制。

 D将可以共享的变化部分,抽离出来,减少了代码的重复信息。

 E对象的具体实现可以更加灵活,可以满足多个因素变化的要求。

    缺点

 A桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

 B桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

    C客户必须知道选择哪一种类型的实现。

4、桥接模式使用场景

    桥接模式使用场景
    A当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。手机品牌有2种变化因素,一个是品牌,一个是功能。

 B当多个变化因素在多个对象间共享时,考虑将变化的部分抽象出来再聚合或组合进来。

 C当考虑一个对象的多个变化因素可以动态变化的时候,考虑使用桥接模式,如手机的手机品牌是变化的,手机的功能也是变化的,所以将每个可变化的因数分离出来,独立的变化。

    抽象工厂模式可以用来创建和配置一个特定的桥接模式。

    适配器模式用来帮助无关的类协同工作,通常在系统设计完成后才会被使用。然而,桥接模式则是在系统开始时就被使用,使得抽象接口和实现部分可以独立进行改变。

       桥接模式和装饰模式在一定程度上都是为了减少子类的数目,避免出现复杂的继承关系但解决的方法却各有不同装饰模式把子类中比基类中多出来的部分放到单独的类里面,以适应新功能增加的需要,当把描述新功能的类封装到基类的对象里面时,就得到所需要的子类对象,描述新功能的类通过组合可以实现很多的功能组合 桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,就可以实现系统在多个维度上的独立变化 。

二、桥接模式实现

Abstraction基类:

#ifndef ABSTRACTION_H

#define ABSTRACTION_H

#include "Implement.h"

 

//抽象基类

class Abstraction

{

public:

    //需要实现的接口

    virtual void request() = 0;

protected:

    Abstraction(Implement* imp):m_implement(imp){}

protected:

    Implement* m_implement;

};

 

#endif // ABSTRACTION_H

RefinedAbstractionA具体类:

#ifndef REFINEDABSTRACTIONA_H

#define REFINEDABSTRACTIONA_H

#include "Abstraction.h"

#include <iostream>

#include "Implement.h"

using namespace std;

 

class RefinedAbstractionA : public Abstraction

{

public:

    RefinedAbstractionA(Implement* imp):Abstraction(imp){}

    virtual void request()

    {

        cout << "RefinedAbstractionA::request" << endl;

        //调用实现部分

        m_implement->operation();

    }

};

 

#endif // REFINEDABSTRACTIONA_H

RefinedAbstractionB具体类:

#ifndef REFINEDABSTRACTIONB_H

#define REFINEDABSTRACTIONB_H

#include "Abstraction.h"

#include <iostream>

#include "Implement.h"

using namespace std;

 

class RefinedAbstractionB : public Abstraction

{

public:

    RefinedAbstractionB(Implement* imp):Abstraction(imp){}

    virtual void request()

    {

        cout << "RefinedAbstractionB::request" << endl;

        //调用实现部分

        m_implement->operation();

    }

};

 

#endif // REFINEDABSTRACTIONB_H

 

Implement抽象实现类:

#ifndef IMPLEMENT_H

#define IMPLEMENT_H

 

//抽象实现类

class Implement

{

public:

    virtual void operation() = 0;

protected:

    Implement(){}

};

 

#endif // IMPLEMENT_H

ConcreteImplementA具体实现类:

#ifndef CONCRETEIMPLEMENTA_H

#define CONCRETEIMPLEMENTA_H

#include "Implement.h"

#include <iostream>

using namespace std;

 

//具体实现类

class ConcreteImplementA : public Implement

{

public:

    //具体实现的功能函数

    void operation()

    {

        cout << "ConcreteImplementA::operation()" << endl;

    }

};

 

#endif // CONCRETEIMPLEMENTA_H

 

ConcreteImplementB具体实现类:

#ifndef CONCRETEIMPLEMENTB_H

#define CONCRETEIMPLEMENTB_H

#include "Implement.h"

#include <iostream>

using namespace std;

 

//具体实现类

class ConcreteImplementB : public Implement

{

public:

    //具体实现的功能函数

    void operation()

    {

        cout << "ConcreteImplementB::operation()" << endl;

    }

};

 

#endif // CONCRETEIMPLEMENTB_H

客户调用程序:

#include "Abstraction.h"

#include "Implement.h"

#include "ConcreteImplementA.h"

#include "ConcreteImplementB.h"

#include "RefinedAbstractionA.h"

#include "RefinedAbstractionB.h"

 

int main()

{

    Implement* imp = new ConcreteImplementA();

    Abstraction* abs = new RefinedAbstractionA(imp);

    abs->request();

 

    Implement* imp1 = new ConcreteImplementB();

    Abstraction* abs1 = new RefinedAbstractionB(imp1);

    abs1->request();

 

    return 0;

}

    将抽象部分与实现部分分离:实现系统可能有多角度(维度)分类,每一种分类都可能变化,把多种角度分离出来让它们独立变化,减少它们之间的耦合。

    在发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,要考虑用桥接模式。

    组合/聚合复用原则:尽量使用组合/聚合,不要使用类继承。
    优先使用对象的组合/聚合将有助于保持每个类被封装,并被集中在单个任务上。类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

三、桥接模式实例

1、计算机实例

电脑品牌和操作系统是两个概念,不同的品牌的电脑可以安装相同或不同的操作系统,两者都具有很大的变动性。如果单独以电脑品牌或操作系统为基类来进行继承扩展的话,会使类的数目剧增并且耦合性很高,如果更改电脑品牌或增加操作系统类型都会增加很多的变动

    将两者抽象出来两个基类分别是ComputerOS,在Computer类中聚合一个OS对象的基类将解决电脑品牌操作系统扩展混乱的问题,两者的扩展就相对灵活,剪短了两者的必要联系

技术分享

Computer接口:

#ifndef COMPUTER_H

#define COMPUTER_H

 

class OS;

class Computer

{

public:

    virtual void installOS(OS* os) = 0;

};

 

#endif // COMPUTER_H

AppleComputer具体实现:

#ifndef APPLECOMPUTER_H

#define APPLECOMPUTER_H

#include "Computer.h"

#include "OS.h"

 

class AppleComputer : public Computer

{

public:

    virtual void installOS(OS* os)

    {

        cout << "AppleComputer ";

        os->installOS_Imp();

    }

};

 

#endif // APPLECOMPUTER_H

 

ThinkPadComputer具体实现:

#ifndef THINKPADCOMPUTER_H

#define THINKPADCOMPUTER_H

#include "Computer.h"

#include "OS.h"

 

class ThinkPadComputer : public Computer

{

public:

    virtual void installOS(OS* os)

    {

        cout << "ThinkPadComputer ";

        os->installOS_Imp();

    }

};

 

#endif // THINKPADCOMPUTER_H

OS接口:

#ifndef OS_H

#define OS_H

#include <iostream>

using namespace std;

 

class OS

{

public:

    virtual void installOS_Imp() = 0;

};

 

#endif // OS_H

 

LinuxOS具体实现:

#ifndef LINUXOS_H

#define LINUXOS_H

#include "OS.h"

 

class LinuxOS : public OS

{

public:

    virtual void installOS_Imp()

    {

        cout << "has installed Linux OS" << endl;

    }

};

 

#endif // LINUXOS_H

 

WindowsOS具体实现:

#ifndef WINDOWSOS_H

#define WINDOWSOS_H

#include "OS.h"

 

class WindowsOS : public OS

{

public:

    virtual void installOS_Imp()

    {

        cout << "has installed Windows OS" << endl;

    }

};

 

#endif // WINDOWSOS_H

客户调用程序:

#include "OS.h"

#include "Computer.h"

#include "LinuxOS.h"

#include "WindowsOS.h"

#include "AppleComputer.h"

#include "ThinkPadComputer.h"

 

int main()

{

    OS* lin = new LinuxOS();

    OS* win = new WindowsOS();

 

    Computer* apple = new AppleComputer();

    Computer* thinkpad = new ThinkPadComputer();

 

    apple->installOS(lin);

    apple->installOS(win);

    thinkpad->installOS(win);

    thinkpad->installOS(lin);

 

    delete lin,win,apple,thinkpad;

    return 0;

}


本文出自 “生命不息,奋斗不止” 博客,谢绝转载!

以上是关于设计模式——桥接模式的主要内容,如果未能解决你的问题,请参考以下文章

桥接模式(Bridge Pattern)

设计模式 -- 桥接模式(Bridge)

设计模式——桥接模式

设计模式实战-桥接模式

设计模式:学习笔记——桥接模式

VMware在桥接模式下无法上网!!!怎么解决?