模式?
這個(gè)問題我用一言兩語實(shí)在無法概括其根本。不過我是這樣分析的:
①、對象這個(gè)概念可以認(rèn)為是由“屬性”和“行為”兩個(gè)部分組成的。屬性我們可以認(rèn)為是一種靜止的,是一種抽象;一般情況下,行為是包含在一個(gè)對象中,但是,在有的情況下,我們需要將這些行為也進(jìn)行歸類,形成一個(gè)總的行為接口,這就是橋模式的用處。
②、Bridge模式是解決多層繼承的。如果你在實(shí)現(xiàn)應(yīng)用中一個(gè)類,需要繼承兩個(gè)以上的類,并且這兩者之間又持有某種關(guān)系,它們兩個(gè)都會(huì)有多種變化。Bridge模式是把這兩個(gè)類,分解為一個(gè)抽象,一個(gè)實(shí)現(xiàn),使它們兩個(gè)分離,這樣兩種類可以獨(dú)立的變化。舉一個(gè)例子:
例如, 比如汽車類(Car),假設(shè)有2個(gè)子類,卡車類(Truck)與公交車類(Bus),它們有[設(shè)置引擎]這個(gè)動(dòng)作行為,通過不同引擎規(guī)格的設(shè)置,可以將它們設(shè)置為比如為1500cc(Car1500),和2000cc(Car2000)的車。
這樣一來,我們怎么來設(shè)計(jì)汽車類呢?
方法一:通過繼承設(shè)計(jì)所有可能存在的子類。可能我們會(huì)想到下面的這種繼承關(guān)系:
汽車總類:Car
汽車子類 - 按種類分類:Bus,Truck
汽車子類 - 按引擎分類:Bus1500,Bus2000,Truck1500,Truck2000
這樣設(shè)置引擎這個(gè)動(dòng)作就由各個(gè)子類加以實(shí)現(xiàn)。
但如果以后需要增加一種救火車(FireCar),以及增加一個(gè)引擎規(guī)格2500cc,需要實(shí)現(xiàn)的子類將會(huì)有:
Bus1500,Bus2000,Bus2500,Truck1500,Truck2000,Truck2500,F(xiàn)ireCar1500,F(xiàn)ireCar2000,F(xiàn)ireCar2500
多達(dá)9個(gè)。
也就是說,這種設(shè)計(jì)方法,子類數(shù)目將隨幾何級數(shù)增長。
而且,Bus1500,Truck1500的引擎規(guī)格相同,它們的引擎設(shè)置動(dòng)作應(yīng)該是一樣的,但現(xiàn)在把它們分成不同的子類,難以避免執(zhí)行重復(fù)的動(dòng)作行為。
方法二:分別為Bus以及Truck實(shí)現(xiàn)設(shè)置不同引擎的方法
汽車總類:Car
汽車子類:Bus,Truck
然后在Bus類里分別提供1500cc以及2000cc引擎的設(shè)置方法:
Bus extends Car {
public setEngine1500cc();
public setEngine2000cc();
}
在Truck類里也分別提供1500cc以及2000cc引擎的設(shè)置方法:
Truck extends Car {
public setEngine1500cc();
public setEngine2000cc();
}
//這樣沒增加一種規(guī)格都要在每種車?yán)镄薷淖宇悆?nèi)容。增加引擎設(shè)置方法,這種牽一發(fā)而動(dòng)全身的做法也非常復(fù)雜。
方法三:橋梁模式。
抽象 - Abstraction類:汽車類及其子類:
Car:汽車總類
Truck:汽車子類 - 卡車類。
Bus:汽車子類 - 公交車類。
行為實(shí)現(xiàn) - Implementor:汽車引擎設(shè)置的行為類及子類
SetCarEngine:汽車引擎的設(shè)置接口
SetCarEngine1500cc:設(shè)置1500cc引擎
SetCarEngine2000cc:設(shè)置2000cc引擎
小結(jié):橋模式就是將抽象和行為劃分開來,各自獨(dú)立,但能動(dòng)態(tài)的結(jié)合。將抽象化與實(shí)現(xiàn)化脫耦,使得二者可以獨(dú)立的變化,也就是說將他們之間的強(qiáng)關(guān)聯(lián)變成弱關(guān)聯(lián),也就是指在一個(gè)軟件系統(tǒng)的抽象化和實(shí)現(xiàn)化之間使用組合/聚合關(guān)系而不是繼承關(guān)系,從而使兩者可以獨(dú)立的變化。
2、使用Bridge模式。
現(xiàn)在需要設(shè)計(jì)兩個(gè)接口:抽象接口和行為接口,分別放置抽象和行為.
那我們從分離抽象和行為的角度,使用Bridge模式來實(shí)現(xiàn)。
從網(wǎng)上抄來的很直觀的方法。
* 任務(wù)敘述:我們現(xiàn)在要實(shí)現(xiàn)一個(gè)畫圖系統(tǒng),這個(gè)系統(tǒng)的需求暫時(shí)來說有以下幾個(gè):
* 1、畫圓、畫長方形。(先實(shí)現(xiàn)畫這兩個(gè)形狀)
* 2、已經(jīng)有兩個(gè)畫圖庫了,這些畫圖庫中定義了我們需要的操作,我們沒必要重新去實(shí)現(xiàn)了
* 3、我們的系統(tǒng)能夠決定使用哪個(gè)的畫圖庫中的操作
* 根據(jù)以上的需求,我們的代碼設(shè)計(jì)如下(這里先不使用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//不使用橋模式代碼
// 看到形狀,很容易想到經(jīng)典的Shape抽象類,并定義一個(gè)Draw接口
// 這里1代表使用畫圖庫1的畫圖操作
class CShape1
{
public:
virtual void Draw() = 0;
protected:
CDrawLib1 _drawLib;
};
// 畫圓和畫長方形,好,這里就派生出兩個(gè)形狀
class CCircle1: public CShape1
{
public:
// 實(shí)現(xiàn)Draw操作,使用畫圖庫1
virtual void Draw()
{
// 使用畫圖庫1
_drawLib.DrawCircle();
}
};
class CRectangle1: public CShape1
{
public:
// 實(shí)現(xiàn)Draw操作,但是要使用畫圖庫1
virtual void Draw()
{
// 使用畫圖庫1
_drawLib.DrawRectangle();
}
};
// 再定義使用畫圖庫2的Shape
class CShape2
{
public:
virtual void Draw() = 0;
protected:
CDrawLib2 _drawLib;
};
// 畫圓和畫長方形,好,這里就派生出兩個(gè)形狀
class CCircle2: public CShape2
{
public:
// 實(shí)現(xiàn)Draw操作,使用畫圖庫2
virtual void Draw()
{
// 使用畫圖庫2
_drawLib.DrawCircle();
}
};
class CRectangle2: public CShape2
{
public:
// 實(shí)現(xiàn)Draw操作,但是要使用畫圖庫2
virtual void Draw()
{
// 使用畫圖庫2
_drawLib.DrawRectangle();
}
};
void Draw1(CShape1 &s)
{
s.Draw();
}
void Draw2(CShape2 &s)
{
s.Draw();
}
// 好了,我們開始使用上面的類來實(shí)現(xiàn)我們的畫圖系統(tǒng)
void main()
{
cout << "不使用Bridge模式" << endl;
// 我可以使用兩種庫
CCircle1 c1;
CCircle2 c2;
CRectangle1 r1;
CRectangle2 r2;
Draw1(c1);
Draw1(r1);
Draw2(c2);
Draw2(r2);
}
/************************************************************************************************************\
* 好的,上述的代碼運(yùn)行正常,如果不需要維護(hù)的話,我們就不用管它拉~~
* 但是,代碼是一定要維護(hù)的,逃不過的宿命。
* 出現(xiàn)變化的地方可能是這樣的:
* 1、出現(xiàn)了第三個(gè)庫
* 2、畫圖系統(tǒng)需要畫三角形
* 這個(gè)時(shí)候,我們再看看要完成這兩個(gè)變化我們需要作的修改,就會(huì)發(fā)現(xiàn),我要暈了
* (當(dāng)一個(gè)程序員要暈的時(shí)候,也就是BUG要出現(xiàn)的時(shí)候了)
\************************************************************************************************************/
#else
/************************************************************************************************************\
* 好了,現(xiàn)在讓我們使用Bridge模式來實(shí)現(xiàn)上面的系統(tǒng)
* Bridge模式最重要是把表示和實(shí)現(xiàn)分開
\************************************************************************************************************/
// 建立一個(gè)實(shí)現(xiàn)操作的類: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();
}
};
// 好了,我們利用上面的類結(jié)構(gòu)來實(shí)現(xiàn)我們的畫圖系統(tǒng)
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();
}
/************************************************************************************************************\
* 好的,上述的代碼運(yùn)行正常,并且維護(hù)也方便了,回想一下上面的兩個(gè)新需求:
* 1、出現(xiàn)了第三個(gè)庫
* 2、畫圖系統(tǒng)需要畫三角形
* 考慮一下,對于第一個(gè)需求,我們只需要再增加一個(gè)ShapeImp就可以了。
* 再看第二個(gè)需求,我們也是只需要派生一個(gè)CTriangle就可以了
* 可以看出來,變化不再造成混亂,只需要單獨(dú)針對變化改動(dòng)代碼就行了。
* 也就是,變化被Bridge給分開了。
\***********************************************************************