定義
將抽象部分與實現部分分離開來,使得二者可以獨立變化,互不影響的結構型設計模式。
主要組成
抽象部分(Abstraction) : 該類保持一個對實現部分的引用,抽象部分中的方法需要呼叫實現部分的物件的方法來實現;這部分一般為抽象類居多。
具體抽象部分(Concrete Abstraction):
抽象部分的具體實現,一般用於抽象部分方法的完善和擴充套件;
實現部分(Implementor) : 可以為介面或抽象類,其方法不一定要與抽象部分中的一致,一般情況下是由實現部分提供基本的操作,而抽象部分定義的則是基於實現部分這些基本操作的業務方法;
具體實現部分(Concrete Implementor): 實現完善"實現部分"定義的相關介面功能;
客戶端(Client): 客戶類,客戶端程式 (呼叫程式的地方)
具體例子
實現部分(Implementor) : Line"線",抽象為介面類,方法為Draw"畫線"。
具體實現部分(Concrete Implementor): 實現Line功能的子類,例如SolidLine繪製實線、DottedLine繪製虛線。
抽象部分(Abstraction): 形狀Shape,持有一個Implementor的引用,並定義出需要藉助Implementor來實現的方法,比如使用“線"繪製出形狀
具體抽象部分(Concrete Abstraction): Abstraction的實現類,完善擴充套件Implementor的方法。比如三角形、矩形、圓形等,繪製出各自的圖形
可以看到Implementor和Abstraction有相對明顯的上下級,Abstraction需要呼叫Implementor來實現目的,是Implementor上級。
UML圖
框架程式碼
實現部分(Implementor):
public interface Line {
void draw();
}
複製程式碼
具體實現部分(Concrete Implementor):
public class SolidLine implements Line {
@Override
public void draw() {
//...
System.out.println("繪製實線");
}
}
複製程式碼
public class GottedLine implements Line {
@Override
public void draw() {
//...
System.out.println("繪製虛線");
}
}
複製程式碼
抽象部分(Abstraction):
public abstract class Shape {
//持有一個實現部分Implementor引用
Line line;
public Shape(Line line) {
this.line = line;
}
public abstract void drawShape();
}
複製程式碼
具體抽象部分(Concrete Abstraction):
public class Circular extends Shape {
public Circular(Line line) {
super(line);
}
@Override
public void drawShape() {
//...
line.draw();
System.out.println("繪製出圓形");
//...
}
}
複製程式碼
public class Rectangle extends Shape {
public Rectangle(Line line) {
super(line);
}
@Override
public void drawShape() {
//...
line.draw();
System.out.println("繪製出矩形");
//...
}
}
複製程式碼
客戶端呼叫示例:
//使用實線繪製圓形
Circular solidCircular = new Circular(new SolidLine());
solidCircular.drawShape();
//使用虛線繪製圓形
Circular gottedCircular = new Circular(new GottedLine());
gottedCircular.drawShape();
//使用實線繪製矩形
Rectangle solidRectangle = new Rectangle(new SolidLine());
solidRectangle.drawShape();
//使用虛線繪製矩形
Rectangle gottedRectangle = new Rectangle(new GottedLine());
gottedRectangle.drawShape();
複製程式碼
假設不使用橋接模式
上述例子假設沒使用橋接模式,很明顯會出現"類爆炸"的問題,類數量出現快速增加:
假設目前只有m=3種線: 實線、虛線、雙實線,需要繪製的形狀為n=2種:圓形、矩形。那麼當實現的類個數為m * n ,以後無論多增加一種形狀(增加m個線數量的實現類),或者多增加一種線(增加n個形狀的實現類),(在基數足夠大的情況下)都會爆炸式的增加太多的實現類。
而如果如上使用橋接模式,無論是增加形狀(抽象部分)、線(實現部分),理論上都只需要增加各自的一個具體實現,實現了抽象部分和實現部分可以獨立修改互不影響。
總結
橋接模式一般用於存在上下級關係的二維變化系統中,將底層的部分分割為實現層,將較上層的部分分割為抽象層。 抽象層中的方法實現實際依賴於實現層物件的方法。
優點
分離抽象和實現部分: 橋接模式分離了抽象和實現部分,從而極大地提高了系統的靈活性。讓抽象部分和實現部分獨立開來,分別定義介面,這有助於對系統進行分層,從而產生更好的結構化的系統。對於系統的高層部分,只需要知道抽象部分和實現部分的介面就可以了。
靈活的擴充套件性: 由於橋接模式把抽象和實現部分分離開了,而且分別定義介面,這就使得抽象部分和實現部分可以分別獨立的擴充套件,而不會相互影響,從而大大的提高了系統的可擴充套件性。可動態切換實現。 由於橋接模式把抽象和實現部分分離開了,那麼在實現橋接的時候,就可以實現動態的選擇和使用具體的實現,也就是說一個實現不再是固定的繫結在一個抽象介面上了,可以實現執行期間動態的切換實現。
缺點
實際應用中相對不容易設計,因為判斷哪部分作為實現部分,哪部分作為抽象部分對開發者要有一定的經驗要求。
使用場景
如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承關係,可以通過橋接模式使他們在抽象層建立一個關聯關係。 對於那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,也可以考慮使用橋接模式。 一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴充套件。
思考:
1.橋接模式可以做到的感覺裝飾者模式好像也可以做到,二者的區別?
裝飾者模式支援使用多個裝飾者多次裝飾被裝飾者,並且要求裝飾者和被裝飾者提供一致的介面行為,介面要求返回的也是統一的被裝飾者介面物件。 而橋接模式一般用一個抽象部分來擴充套件完善實現部分,並不要求提供一致的介面行為。
裝飾者模式一般沒有明顯的上下級依賴關係,但是橋接模式的2個維度往往會有上下級依賴關係的存在。
2.怎麼判斷哪部分作為實現部分,哪部分作為抽象部分?
一般是真正的"實現"部分(被依賴的部分)作為實現部分,上層作為抽象部分,可以從例子中理會,因為要繪製形狀需要依賴於繪製線,繪製線其實是真正的實現部分,繪製形狀只是把繪製的線進行拼接(擴充套件完善了線的功能作用,封裝業務功能等)。所以線是實現部分,形狀是抽象部分。