Bridge模式详解--设计模式(6)

Bridge模式的产生原因:

         总结面向对象实际上就两句话:一是松耦合(Coupling),二是高内聚(Cohesion)。面向对象系统追求的目标就是尽可能地提高系统模块内部的内聚(Cohesion)、尽可能降低模块间的耦合(Coupling)。然而这也是面向对象设计过程中最为难把握的部分,大家肯定在OO系统的开发过程中遇到这样的问题:

(1).客户给了你一个需求,于是使用一个类来实现(A);

(2).客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1和A2);

(3).客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00和A01,其中A00表示原来的类A)实现不同操作系统上的客户需求,这样我们就有了一共4个类。

(4).可能用户的需求又有变化,比如说又有了一种新的算法……..

(5).我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。

Bridge模式则正是解决了这类问题。

Bridge模式的作用:

        作用:将抽象部份与它的实现部份分离,使它们都可以独立地变化。将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化。桥接模式号称设计模式中最难理解的模式之一,关键就是这个抽象和实现的分离非常让人奇怪,大部分人刚看到这个定义的时候都会认为实现就是继承自抽象,那怎么可能将他们分离呢。

       《大话设计模式》中就Bridge模式的解释:

        手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高,将两者抽象出来两个基类分别是PhoneBrand和PhoneSoft,那么在品牌类中聚合一个软件对象的基类将解决软件和手机扩展混乱的问题,这样两者的扩展就相对灵活,剪短了两者的必要联系,结构图如下:

                                 Bridge模式详解--设计模式(6)_第1张图片

Bridge模式的UML结构图如图1所示:

                

Bridge模式构成

1、Abstraction::Operation():定义要实现的操作接口

2、AbstractionImplement::Operation():实现抽象类Abstaction所定义操作的接口,由其具体派生类ConcreteImplemenA、ConcreteImplemenA或者其他派生类实现。

3、在Abstraction::Operation()中根据不同的指针多态调用AbstractionImplement::Operation()函数。

理解:
Bridge用于将表示和实现解耦,两者可以独立的变化.在Abstraction类中维护一个AbstractionImplement类指针,需要采用不同的实现方式的时候只需要传入不同的AbstractionImplement派生类就可以了.

Bridge的实现方式其实和Builde十分的相近,可以这么说:本质上是一样的,只是封装的东西不一样罢了.两者的实现都有如下的共同点:

抽象出来一个基类,这个基类里面定义了共有的一些行为,形成接口函数(对接口编程而不是对实现编程),这个接口函数在Buildier中是BuildePart函数在Bridge中是Operation函数;

其次,聚合一个基类的指针,如Builder模式中Director类聚合了一个Builder基类的指针,而Brige模式中Abstraction类聚合了一个AbstractionImplement基类的指针(优先采用聚合而不是继承);

而在使用的时候,都把对这个类的使用封装在一个函数中,在Bridge中是封装在Director::Construct函数中,因为装配不同部分的过程是一致的,而在Bridge模式中则是封装在Abstraction::Operation函数中,在这个函数中调用对应的AbstractionImplement::Operation函数.就两个模式而言,Builder封装了不同的生成组成部分的方式,而Bridge封装了不同的实现方式.

桥接模式就将实现与抽象分离开来,使得RefinedAbstraction依赖于抽象的实现,这样实现了依赖倒转原则,而不管左边的抽象如何变化,只要实现方法不变,右边的具体实现就不需要修改,而右边的具体实现方法发生变化,只要接口不变,左边的抽象也不需要修改。

Bridge模式典型的示例代码如下:

#include <iostream>
#include <string>
using namespace std;

class HandsetSoft
{
public:
	virtual void run()=0;
};

class HandsetGame:public HandsetSoft
{
public:
	void run()
	{
		cout<<"运行手机游戏"<<endl;
	}
};

class HandsetAddressList:public HandsetSoft
{
public:
	void run()
	{
		cout<<"运行手机通讯录"<<endl;
	}
};

class HandsetBrand
{
protected:
	HandsetSoft *soft;
public:
	void setHandsetSoft(HandsetSoft *soft)
	{
		this->soft=soft;
	}
	virtual void run()=0;
};

class HandsetBrandN:public HandsetBrand
{
public:
	void run()
	{
		soft->run();
	}
};

class HandsetBrandM:public HandsetBrand
{
public:
	void run()
	{
		soft->run();
	}
};

int main()
{
	HandsetBrand *hb;
	hb=new HandsetBrandM();
	
	hb->setHandsetSoft(new HandsetGame());
	hb->run();
	hb->setHandsetSoft(new HandsetAddressList());
	hb->run();


	return 0;
}

 Bridge模式适用场景:

1.当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。如上面例子中手机品牌有2种变化因素,一个是品牌,一个是功能。

2.当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来,如上面例子中的通讯录和游戏,其实是可以共享的。

3.当我们考虑一个对象的多个变化因素可以动态变化的时候,考虑使用桥接模式,如上面例子中的手机品牌是变化的,手机的功能也是变化的,所以将他们分离出来,独立的变化。

总结:

1.设计中有超过一维的变化我们就可以用桥模式。如果只有一维在变化,那么我们用继承就可以圆满的解决问题。

Abstraction.h

#ifndef _ABSTRACTION_H_
#define _ABSTRACTION_H_

class AbstractionImplement;

class Abstraction
{
public:
    virtual void Operation()=0;//定义接口,表示该类所支持的操作
    virtual ~Abstraction();
protected:
    Abstraction();
};

class RefinedAbstractionA:public Abstraction
{
public:
    RefinedAbstractionA(AbstractionImplement* imp);//构造函数
    virtual void Operation();//实现接口
    virtual ~RefinedAbstractionA();//析构函数
private:
    AbstractionImplement* _imp;//私有成员
};

class RefinedAbstractionB:public Abstraction
{
public:
    RefinedAbstractionB(AbstractionImplement* imp);//构造函数
    virtual void Operation();//实现接口
    virtual ~RefinedAbstractionB();//析构函数
private:
    AbstractionImplement* _imp;//私有成员
};
#endif

Abstraction.cpp

#include "Abstraction.h"
#include "AbstractionImplement.h"
#include <iostream>

using namespace std;

Abstraction::Abstraction()
{}

Abstraction::~Abstraction()
{}

RefinedAbstractionA::RefinedAbstractionA(AbstractionImplement* imp)
{
    this->_imp = imp;
}

RefinedAbstractionA::~RefinedAbstractionA()
{
    delete this->_imp;
    this->_imp = NULL;
}

void RefinedAbstractionA::Operation()
{
    cout << "RefinedAbstractionA::Operation" << endl;
    this->_imp->Operation();
}

RefinedAbstractionB::RefinedAbstractionB(AbstractionImplement* imp)
{
    this->_imp = imp;
}

RefinedAbstractionB::~RefinedAbstractionB()
{
    delete this->_imp;
    this->_imp = NULL;
}

void RefinedAbstractionB::Operation()
{
    cout << "RefinedAbstractionB::Operation" << endl;
    this->_imp->Operation();
}
AbstractImplement.h

#ifndef _ABSTRACTIONIMPLEMENT_H_
#define _ABSTRACTIONIMPLEMENT_H_

//抽象基类,定义了实现的接口
class AbstractionImplement
{
public:
    virtual void Operation()=0;//定义操作接口
    virtual ~AbstractionImplement();
protected:
    AbstractionImplement();
};

// 继承自AbstractionImplement,是AbstractionImplement的不同实现之一
class ConcreteAbstractionImplementA:public AbstractionImplement
{
public:
    ConcreteAbstractionImplementA();
    void Operation();//实现操作
    ~ConcreteAbstractionImplementA();
protected:
};

// 继承自AbstractionImplement,是AbstractionImplement的不同实现之一
class ConcreteAbstractionImplementB:public AbstractionImplement
{
public:
    ConcreteAbstractionImplementB();
    void Operation();//实现操作
    ~ConcreteAbstractionImplementB();
protected:
};
#endif

AbstractImplement.cpp

#include "AbstractionImplement.h"
#include <iostream>

using namespace std;

AbstractionImplement::AbstractionImplement()
{}

AbstractionImplement::~AbstractionImplement()
{}

ConcreteAbstractionImplementA::ConcreteAbstractionImplementA()
{}

ConcreteAbstractionImplementA::~ConcreteAbstractionImplementA()
{}

void ConcreteAbstractionImplementA::Operation()
{
    cout << "ConcreteAbstractionImplementA Operation" << endl;
}

ConcreteAbstractionImplementB::ConcreteAbstractionImplementB()
{}

ConcreteAbstractionImplementB::~ConcreteAbstractionImplementB()
{}

void ConcreteAbstractionImplementB::Operation()
{
    cout << "ConcreteAbstractionImplementB Operation" << endl;
}

main.cpp

#include "Abstraction.h"
#include "AbstractionImplement.h"
#include <iostream>

using namespace std;

int main()
{
    /* 将抽象部分与它的实现部分分离,使得它们可以独立地变化

    1、抽象Abstraction与实现AbstractionImplement分离;

    2、抽象部分Abstraction可以变化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2);

    3、实现部分AbstractionImplement也可以变化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB();

    */

    AbstractionImplement* imp = new ConcreteAbstractionImplementA();        //实现部分ConcreteAbstractionImplementA
    Abstraction* abs = new RefinedAbstractionA(imp);                        //抽象部分RefinedAbstractionA
    abs->Operation();

    cout << "-----------------------------------------" << endl;

    AbstractionImplement* imp1 = new ConcreteAbstractionImplementB();        //实现部分ConcreteAbstractionImplementB
    Abstraction* abs1 = new RefinedAbstractionA(imp1);                        //抽象部分RefinedAbstractionA
    abs1->Operation();

    cout << "-----------------------------------------" << endl;

    AbstractionImplement* imp2 = new ConcreteAbstractionImplementA();        //实现部分ConcreteAbstractionImplementA
    Abstraction* abs2 = new RefinedAbstractionB(imp2);                        //抽象部分RefinedAbstractionB
    abs2->Operation();

    cout << "-----------------------------------------" << endl;

    AbstractionImplement* imp3 = new ConcreteAbstractionImplementB();        //实现部分ConcreteAbstractionImplementB
    Abstraction* abs3 = new RefinedAbstractionB(imp3);                        //抽象部分RefinedAbstractionB
    abs3->Operation();

    cout << endl;
    return 0;
}

Bridge模式优缺点总结:

优点:
1.将实现抽离出来,再实现抽象,使得对象的具体实现依赖于抽象,满足了依赖倒转原则。

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

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

缺点:
1.客户必须知道选择哪一种类型的实现。

Bridge模式使用总结:

        Bridge模式将抽象和实现分别独立实现,在代码中就是Abstraction类和AbstractionImplement类。

使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。
       GoF的那句话中的“实现”该怎么去理解:“实现”特别是和“抽象”放在一起的时候我们“默认”的理解是“实现”就是“抽象”的具体子类的实现,但是这里GoF所谓的“实现”的含义不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是和继承结合在一起的。而这里的“实现”的含义指的是怎么去实现用户的需求,并且指的是通过组合(委托)的方式实现的,因此这里的实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。

        实际上上面使用Bridge模式和使用带来问题方式的解决方案的根本区别在于是通过继承还是通过组合的方式去实现一个功能需求。

备注:

        由于实现的方式有多种,桥接模式的核心就是把这些实现独立出来,让他们各自变化。将抽象部分与它的实现部分分离:实现系统可能有多角度(维度)分类,每一种分类都可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。在发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,就要考虑用Bridge桥接模式了。合成/聚合复用原则:尽量使用合成/聚合,精良不要使用类继承。
        优先使用对象的合成/聚合将有助于保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。



你可能感兴趣的:(C++)