posts - 34,comments - 2,trackbacks - 0
1、什么是Bridge模式?
这个问题我用一言两语实在无法概括其根本。不过我是这样分析的:
①、对象这个概念可以认为是由“属性”和“行为”两个部分组成的。属性我们可以认为是一种静止的,是一种抽象;一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。
②、Bridge模式是解决多层继承的。如果你在实现应用中一个类,需要继承两个以上的类,并且这两者之间又持有某种关系,它们两个都会有多种变化。Bridge模式是把这两个类,分解为一个抽象一个实现,使它们两个分离,这样两种类可以独立的变化。举一个例子:

例如, 比如汽车类(Car),假设有2个子类,卡车类(Truck)与公交车类(Bus),它们有[设置引擎]这个动作行为,通过不同引擎规格的设置,可以将它们设置为比如为1500cc(Car1500),和2000cc(Car2000)的车。

这样一来,我们怎么来设计汽车类呢?
方法一:通过继承设计所有可能存在的子类。可能我们会想到下面的这种继承关系:
汽车总类:Car
汽车子类 - 按种类分类:Bus,Truck
汽车子类 - 按引擎分类:Bus1500,Bus2000,Truck1500,Truck2000
这样设置引擎这个动作就由各个子类加以实现。

但如果以后需要增加一种救火车(FireCar),以及增加一个引擎规格2500cc,需要实现的子类将会有:
Bus1500,Bus2000,Bus2500,Truck1500,Truck2000,Truck2500,FireCar1500,FireCar2000,FireCar2500
多达9个。
也就是说,这种设计方法,子类数目将随几何级数增长。
而且,Bus1500,Truck1500的引擎规格相同,它们的引擎设置动作应该是一样的,但现在把它们分成不同的子类,难以避免执行重复的动作行为。

方法二:分别为Bus以及Truck实现设置不同引擎的方法
汽车总类:Car
汽车子类:Bus,Truck
然后在Bus类里分别提供1500cc以及2000cc引擎的设置方法:
Bus extends Car {
    public setEngine1500cc();
    public setEngine2000cc();
}
在Truck类里也分别提供1500cc以及2000cc引擎的设置方法:
Truck extends Car {
    public setEngine1500cc();
    public setEngine2000cc();
}
//这样没增加一种规格都要在每种车里修改子类内容。增加引擎设置方法,这种牵一发而动全身的做法也非常复杂。
方法三:桥梁模式。
抽象 - Abstraction类:汽车类及其子类:
Car:汽车总类
Truck:汽车子类 - 卡车类。
Bus:汽车子类 - 公交车类。

行为实现 - Implementor:汽车引擎设置的行为类及子类
SetCarEngine:汽车引擎的设置接口
SetCarEngine1500cc:设置1500cc引擎
SetCarEngine2000cc:设置2000cc引擎
小结:桥模式就是将抽象和行为划分开来,各自独立,但能动态的结合。将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

2、使用Bridge模式
现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为.

那我们从分离抽象和行为的角度,使用Bridge模式来实现。
从网上抄来的很直观的方法。
* 任务叙述:我们现在要实现一个画图系统,这个系统的需求暂时来说有以下几个: 

* 1、画圆、画长方形。(先实现画这两个形状)  

* 2、已经有两个画图库了,这些画图库中定义了我们需要的操作,我们没必要重新去实现了 

* 3、我们的系统能够决定使用哪个的画图库中的操作 

* 根据以上的需求,我们的代码设计如下(这里先不使用Bridge模式,以便对比) : 

#include <stdio.h>  

#include <iostream>  

using namespace std;  

// 把这里注释掉就是不使用Bridge模式的代码  

#define _BRIDGE_MODE  

// 画图库1  

class CDrawLib1  

{  

public:  

    void DrawCircle()  

    {  

        cout << "画圆操作1" << endl;  

    }  

    void DrawRectangle()  

    {  

        cout << "画长方形操作1" << endl;  

    }  

    void DrawTriangle()  

    {  

        cout << "画三角形操作1" << endl;  

    }  

};  

// 画图库2  

class CDrawLib2  

{  

public:  

    void DrawCircle()  

    {  

        cout << "画圆操作2" << endl;  

    }  

     

    void DrawRectangle()  

    {  

        cout << "画长方形操作2" << endl;  

    }  

    void DrawTriangle()  

    {  

        cout << "画三角形操作2" << endl;  

    }         

};   

//  只要声明 _BRIDGE_MODE 就可以在使用或者不使用Bridge模式间切换了  

#ifndef _BRIDGE_MODE//不使用桥模式代码  

// 看到形状,很容易想到经典的Shape抽象类,并定义一个Draw接口  

// 这里1代表使用画图库1的画图操作  

class CShape1  

{  

public:  

    virtual void Draw() = 0;  

protected:  

    CDrawLib1 _drawLib;  

};  

// 画圆和画长方形,好,这里就派生出两个形状  

class CCircle1: public CShape1  

{  

public:  

    // 实现Draw操作,使用画图库1  

    virtual void Draw()  

    {  

        // 使用画图库1  

        _drawLib.DrawCircle();    

    }  

};  

class CRectangle1: public CShape1  

{   

public:  

    // 实现Draw操作,但是要使用画图库1  

    virtual void Draw()  

    {  

        // 使用画图库1  

        _drawLib.DrawRectangle();     

    }  

};  

// 再定义使用画图库2Shape  

class CShape2  

{  

public:  

    virtual void Draw() = 0;  

protected:  

    CDrawLib2 _drawLib;  

};  

// 画圆和画长方形,好,这里就派生出两个形状  

class CCircle2: public CShape2  

{  

public:  

    // 实现Draw操作,使用画图库2  

    virtual void Draw()  

    {  

        // 使用画图库2  

        _drawLib.DrawCircle();    

    }  

};  

class CRectangle2: public CShape2  

{  

public:  

    // 实现Draw操作,但是要使用画图库2  

    virtual void Draw()  

    {  

        // 使用画图库2  

        _drawLib.DrawRectangle();     

    }  

};  

void Draw1(CShape1 &s)  

{  

    s.Draw();  

}  

void Draw2(CShape2 &s)  

{  

    s.Draw();  

}  

// 好了,我们开始使用上面的类来实现我们的画图系统  

void main()  

{  

    cout << "不使用Bridge模式" << endl;  

      

    // 我可以使用两种库  

    CCircle1 c1;  

    CCircle2 c2;  

    CRectangle1 r1;  

    CRectangle2 r2;  

    Draw1(c1);  

    Draw1(r1);  

    Draw2(c2);  

    Draw2(r2);  

}  

/************************************************************************************************************\ 

* 好的,上述的代码运行正常,如果不需要维护的话,我们就不用管它拉~~ 

* 但是,代码是一定要维护的,逃不过的宿命。 

* 出现变化的地方可能是这样的: 

* 1、出现了第三个库 

* 2、画图系统需要画三角形 

* 这个时候,我们再看看要完成这两个变化我们需要作的修改,就会发现,我要晕了 

* (当一个程序员要晕的时候,也就是BUG要出现的时候了) 

\************************************************************************************************************/ 

#else  

/************************************************************************************************************\ 

* 好了,现在让我们使用Bridge模式来实现上面的系统 

* Bridge模式最重要是把表示和实现分开 

\************************************************************************************************************/ 

// 建立一个实现操作的类:CShapeImp  

class CShapeImp  

{  

public:  

    virtual void DrawCircle() = 0;  

    virtual void DrawRectangle() = 0;  

};  

class CShapeImp1: public CShapeImp  

{  

public:  

    virtual void DrawCircle()  

    {  

        _dLib.DrawCircle();  

    }  

    virtual void DrawRectangle()  

    {  

        _dLib.DrawRectangle();  

    }  

private:  

    CDrawLib1 _dLib;  

};  

class CShapeImp2: public CShapeImp  

{  

public:  

    virtual void DrawCircle()  

    {  

        _dLib.DrawCircle();  

    }  

    virtual void DrawRectangle()  

    {  

        _dLib.DrawRectangle();  

    }  

private:  

    CDrawLib2 _dLib;      

};  

class CShape  

{  

public:  

    virtual void Draw() = 0;  

protected:  

    CShapeImp *_sImp;  

};  

class CCircle: public CShape  

{  

public:  

    CCircle(CShapeImp *imp)  

    {  

        _sImp = imp;  

    }  

    virtual void Draw()  

    {  

        _sImp->DrawCircle();  

    }     

};  

class CRectangle: public CShape  

{   

public:  

    CRectangle(CShapeImp *imp)  

    {  

        _sImp = imp;  

    }     

    virtual void Draw()  

    {  

        _sImp->DrawRectangle();  

    }  

};  

// 好了,我们利用上面的类结构来实现我们的画图系统  

void main()  

{  

    cout << "使用Bridge模式" << endl;  

    CShapeImp1 sImp1;  

    CShapeImp2 sImp2;  

      

    // 使用画图库1  

    CCircle c1(&sImp1);  

    CRectangle r1(&sImp1);  

    c1.Draw();  

    r1.Draw();  

      

    // 使用画图库2  

    CCircle c2(&sImp2);  

    CRectangle r2(&sImp2);  

    c2.Draw();  

    r2.Draw();  

}  

/************************************************************************************************************\ 

* 好的,上述的代码运行正常,并且维护也方便了,回想一下上面的两个新需求: 

* 1、出现了第三个库 

* 2、画图系统需要画三角形 

* 考虑一下,对于第一个需求,我们只需要再增加一个ShapeImp就可以了。 

* 再看第二个需求,我们也是只需要派生一个CTriangle就可以了 

* 可以看出来,变化不再造成混乱,只需要单独针对变化改动代码就行了。 

* 也就是,变化被Bridge给分开了。 

\***********************************************************************

posted on 2011-09-27 18:42 Yu_ 阅读(317) 评论(0)  编辑 收藏 引用 所属分类: GoF23

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理