模式?
这个问题我用一言两语实在无法概括其根本。不过我是这样分析的:
①、对象这个概念可以认为是由“属性”和“行为”两个部分组成的。属性我们可以认为是一种静止的,是一种抽象;一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。
②、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();
}
};
// 再定义使用画图库2的Shape
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给分开了。
\***********************************************************************