感謝《Android源碼設計模式解析與實戰》 何紅輝 關愛民 著
橋接模式(Bridge Pattern)也稱爲橋樑模式,是結構型設計模式之一,“橋樑”是連接河道兩岸的主要交通樞紐,而橋接模式與現實中的情況很相似,也是承擔着連接“兩邊”的作用。
定義:
將抽象部分與實現部分分離,使它們都可以獨立地進行變化。
使用場景:
任何多維度變化類或者說多個樹狀類之間的耦合都可以使用橋接模式來實現解耦。
如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承關係,可以通過橋接模式使它們在抽象層建立一個關聯關係。
對於那些不希望使用繼承或因爲多層次繼承導致系統類的個數急劇增加的系統,也可以考慮使用橋接模式。
一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展。
UML類圖:
Abstraction:抽象部分,該類保持一個對實現部分對象的引用,抽象部分中的方法需要調用實現部分的對象來實現,該類一般爲抽象類。
RefinedAbstraction:優化的抽象部分,抽象部分的具體實現,該類一般是對抽象部分的方法進行完善和擴展。
Implementor:實現部分,可以爲接或抽象類,其方法不一定要與抽象部分中的一致,一般情況下是由實現部分提供基本的操作,而抽象部分定義的則是基於實現部分這些基本操作的業務方法。
ConcreteInplementorA/B : 實現部分的具體實現,完善實現部分中方法定義的具體邏輯。
示例:
以咖啡爲例,對一杯咖啡來說這4種實質上就兩種變化,一是大杯小杯,二是加糖不加糖。
public abstract class Coffee {
public CoffeeAdditives impl;
public Coffee(CoffeeAdditives impl) {
this.impl = impl;
}
//咖啡具體是什麼樣的由子類決定
public abstract void makeCoffee();
}
Coffee類中保持了對CoffeeAdditives的引用,以便調用具體的實現。
public class LargeCoffee extends Coffee{
public LargeCoffee(CoffeeAdditives impl) {
super(impl);
}
@Override
public void makeCoffee() {
System.out.println("大杯的" + impl.addSomething() + "咖啡");
}
}
public class SmallCoffee extends Coffee{
public SmallCoffee(CoffeeAdditives impl) {
super(impl);
// TODO Auto-generated constructor stub
}
@Override
public void makeCoffee() {
System.out.println("小杯的" + impl.addSomething() + "咖啡");
}
}
對於加進咖啡中的糖,當然也可以選擇不加,也定義一個抽象類
public abstract class CoffeeAdditives {
/**
* 具體要往咖啡裏添加什麼也是由子類實現
* @return
*/
public abstract String addSomething();
}
CoffeeAdditives對應於類圖中的實現部分,Coffee則對應於抽象部分,模式定義中所謂的“抽象”與“實現”實現上對應的是兩個獨立變化的維度,任何多維度變化類或者說多個樹狀類之間的耦合都可以使用橋接模式來實現解耦。本例中,Coffee類雖是一個抽象類,但它並非是所謂的“抽象部分”,而CoffeeAdditives類也並非一定就是“實現部分”,兩者各自爲一維度,獨立變化,所謂的“抽象與實現分離”更偏向於我們實際的程序開發,兩者並不一定掛鉤。
public class Sugar extends CoffeeAdditives{
@Override
public String addSomething() {
return "加糖";
}
}
public class Ordinary extends CoffeeAdditives{
@Override
public String addSomething() {
return "原味";
}
}
public class Client {
public static void main(String[] args) {
//原味
Ordinary implOrdinary = new Ordinary();
//準備糖類
Sugar implSugar = new Sugar();
//大杯 原味
LargeCoffee largeCoffeeOrdinary = new LargeCoffee(implOrdinary);
largeCoffeeOrdinary.makeCoffee();
//小杯原味
SmallCoffee smallCoffeeOrdinary = new SmallCoffee(implOrdinary);
smallCoffeeOrdinary.makeCoffee();
//大杯加糖
LargeCoffee largeCoffeeSugar = new LargeCoffee(implSugar);
largeCoffeeSugar.makeCoffee();
//小杯加糖
SmallCoffee smallCoffeeSugar = new SmallCoffee(implSugar);
smallCoffeeSugar.makeCoffee();
}
//運行結果:
/*大杯的原味咖啡
小杯的原味咖啡
大杯的加糖咖啡
小杯的加糖咖啡*/
}
如果此時爲了滿足更多人的習慣,推出中杯的咖啡怎麼辦? 對應於本例來說,這種需求的變化其實就是Coffee類的變化,定義箇中杯類擴展Coffee類即可
public class MiddleCoffee extends Coffee{
public MiddleCoffee(CoffeeAdditives impl) {
super(impl);
}
@Override
public void makeCoffee() {
System.out.println("中杯的" + impl.addSomething() + "咖啡");
}
}
Client類中修改代碼即可:
//中杯加糖
MiddleCoffee middleCoffeeSugar = new MiddleCoffee(implSugar);
middleCoffeeSugar.makeCoffee();
//中杯原味
MiddleCoffee middleCoffeeOrdinary = new MiddleCoffee(implOrdinary);
middleCoffeeOrdinary.makeCoffee();
爲了增加咖啡口味的種類,我們也可以讓CoffeeAdditives類變化起來,增加更多的子類表示 加奶....等等,從本例中我們可以看到,不管是Coffee變化了還是CoffeeAdditives變化了,其相對於雙方而言都是獨立的,沒有什麼過多的交集,兩者之間唯一的聯繫就是Coffee中保持對CoffeeAdditives的引用,此爲兩者之間的紐帶,這就是橋接模式。
總結:
橋接模式可以應用到些許開發中,但是它應用得卻不多,一個很重要的原因是對於抽象與實現的分離的把握,是不是需要分離,如何分離,對設計者來說要有一個恰到好處的分寸,優點就是分離抽象與實現、靈活的擴展以及對客戶來說透明的實現等。缺點就是不容易設計,對於開發者來說要有一定的經驗要求,因爲,對橋接模式應用來說,理解很簡單,設計卻不容易。