Bridge定義:將抽象和行為划分開來,各自獨立,但能動態的結合。
為什么使用橋模式
通常,當一個抽象類或接口有多個具體實現(concrete subclass),這些concrete之間關系可能有以下兩種:
- 這多個具體實現之間恰好是並列的,如前面舉例,打樁,有兩個concrete class:方形樁和圓形樁;這兩個形狀上的樁是並列的,沒有概念上的重復,那么我們只要使用繼承就可以了。實際應用上,常常有可能在這多個concrete class之間有概念上重疊。那么需要我們把抽象共同部分和行為共同部分各自獨立開來,原來是准備放在一個接口里,現在需要設計兩個接口,分別放置抽象和行為。
例如,一杯咖啡為例,有中杯和大杯之分,同時還有加奶 不加奶之分。如果用單純的繼承,這四個具體實現(中杯 大杯 加奶 不加奶)之間有概念重疊,因為有中杯加奶,也有中杯不加奶,如果再在中杯這一層再實現兩個繼承,很顯然混亂,擴展性極差。那我們使用Bridge模式來實現它。如何實現橋模式
以上面提到的咖啡 為例。我們原來打算只設計一個接口(抽象類),使用Bridge模式后,我們需要將抽象和行為分開,加奶和不加奶屬於行為,我們將它們抽象成一個專門的行為接口。
先看看抽象部分的接口代碼:
public abstract class Coffee{
CoffeeImp coffeeImp;
public void setCoffeeImp() {
this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
}
public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}
public abstract void pourCoffee();
}
其中CoffeeImp 是加不加奶的行為接口,看其代碼如下:
public abstract class CoffeeImp{
public abstract void pourCoffeeImp();
}
現在我們有了兩個抽象類,下面我們分別對其進行繼承,實現concrete class: //中杯
public class MediumCoffee extends Coffee{
public MediumCoffee() {setCoffeeImp();}
public void pourCoffee(){
CoffeeImp coffeeImp = this.getCoffeeImp();
//我們以重復次數來說明是沖中杯還是大杯 ,重復2次是中杯
for (int i = 0; i < 2; i++){
coffeeImp.pourCoffeeImp();
}
}
}
//大杯
public class SuperSizeCoffee extends Coffee{
public SuperSizeCoffee() {setCoffeeImp();}
public void pourCoffee(){
CoffeeImp coffeeImp = this.getCoffeeImp();
//我們以重復次數來說明是沖中杯還是大杯 ,重復5次是大杯
for (int i = 0; i < 5; i++){
coffeeImp.pourCoffeeImp();
}
}
} 上面分別是中杯和大杯的具體實現.下面再對行為CoffeeImp進行繼承: //加奶
public class MilkCoffeeImp extends CoffeeImp{
MilkCoffeeImp() {}
public void pourCoffeeImp(){
System.out.println("加了美味的牛奶");
}
}
//不加奶
public class FragrantCoffeeImp extends CoffeeImp{
FragrantCoffeeImp() {}
public void pourCoffeeImp(){
System.out.println("什么也沒加,清香");
}
} Bridge模式的基本框架我們已經搭好了,別忘記定義中還有一句:動態結合,我們現在可以喝到至少四種咖啡:- 中杯加奶中杯不加奶大杯加奶大杯不加奶
看看是如何動態結合的,在使用之前,我們做個准備工作,設計一個單態類(Singleton)用來hold當前的CoffeeImp: public class CoffeeImpSingleton{
private static CoffeeImp coffeeImp;
public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
{this.coffeeImp = coffeeImpIn;}
public static CoffeeImp getTheCoffeeImp(){
return coffeeImp;
}
} 看看中杯加奶 和大杯加奶 是怎么出來的: //拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());
//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();
//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee(); 注意:Bridge模式的執行類如CoffeeImp和Coffee是一對一的關系,正確創建CoffeeImp是該模式的關鍵。 -
實例2.我們假設有一座橋,橋左邊為A,橋右邊為B,A有A1,A2,A3等,表示橋左邊的三個不同地方,B有B1,B2,B3等,表示橋右邊的三個不同地方,假設我們要從橋左側A出發到橋的右側B,我們可以有多重方案,A1到B1,A1到B2,A1到B3,A2到B1...等等,以此為例
橋接口:Qiao
1 public interface Qiao { 2 //目的地B 3 void targetAreaB(); 4 }
目的地B1,B2,B3:
1 /** 2 * 目的地B1 3 */ 4 public class AreaB1 implements Qiao { 5 6 @Override 7 public void targetAreaB() { 8 System.out.println("我要去B1"); 9 } 10 11 } 12 13 /** 14 * 目的地B2 15 */ 16 public class AreaB2 implements Qiao { 17 18 @Override 19 public void targetAreaB() { 20 System.out.println("我要去B2"); 21 } 22 23 } 24 25 /** 26 * 目的地B3 27 */ 28 public class AreaB3 implements Qiao { 29 30 @Override 31 public void targetAreaB() { 32 System.out.println("我要去B3"); 33 } 34 35 }
抽象來源地A:AreaA
1 public abstract class AreaA { 2 //引用橋接口 3 Qiao qiao; 4 //來源地 5 abstract void fromAreaA(); 6 }
來源地A1,A2,A3:
1 /** 2 * 來源地A1 3 */ 4 public class AreaA1 extends AreaA { 5 6 @Override 7 void fromAreaA() { 8 System.out.println("我來自A1"); 9 } 10 11 } 12 13 /** 14 * 來源地A2 15 */ 16 public class AreaA2 extends AreaA { 17 18 @Override 19 void fromAreaA() { 20 System.out.println("我來自A2"); 21 } 22 23 } 24 25 /** 26 * 來源地A3 27 */ 28 public class AreaA3 extends AreaA { 29 30 @Override 31 void fromAreaA() { 32 System.out.println("我來自A3"); 33 } 34 35 }
測試類:Clienter
1 public class Clienter { 2 public static void main(String[] args) { 3 AreaA a = new AreaA2(); 4 a.qiao = new AreaB3(); 5 a.fromAreaA(); 6 a.qiao.targetAreaB(); 7 } 8 }
運行結果:
我來自A2 我要去B3
如何,只要你認真看完了實例,你就明白了這種模式的好處,現在我們要添加來源地和目的地,只要繼續繼承AreaA和實現Qiao即可,之前我所說的綁定,正式此處將橋與目的地綁定在一起,使用一個接口完成。
其實要完成橋接模式,注意點並不多,重在理解模式的使用場景。
注意點:
1、定義一個橋接口,使其與一方綁定,這一方的擴展全部使用實現橋接口的方式。
2、定義一個抽象類,來表示另一方,在這個抽象類內部要引入橋接口,而這一方的擴展全部使用繼承該抽象類的方式。
其實我們可以發現橋接模式應對的場景有方向性的,橋綁定的一方都是被調用者,屬於被動方,抽象方屬於主動方。
其實我的JDK提供的JDBC數據庫訪問接口API正是經典的橋接模式的實現者,接口內部可以通過實現接口來擴展針對不同數據庫的具體實現來進行擴展,而對外的僅僅只是一個統一的接口調用,調用方過於抽象,可以將其看做每一個JDBC調用程序(這是真實實物,當然不存在抽象)
下面來理解一下概念:
橋接(Bridge)是用於把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬於結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。
這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立於接口實現類。這兩種類型的類可被結構化改變而互不影響。
理解:此處抽象化與實現化分別指代實例中的雙方,而且實現化對應目的地方(通過實現橋接口進行擴展),抽象方對應來源地方(通過繼承抽象類來進行擴展),如果我們不使用橋接模式,我們會怎么想實現這個實例呢?很簡單,我們分別定義來源地A1、A2、A3類和目的地B1、B2、B3,然后具體的實現就是,A1到B1一個類,A1到B2一個類,等,如果我們要擴展了A和B ,要直接增加An類和Bn類,如此編寫不說類內部重復性代碼多,而且還會導致類結構的急劇膨脹,最重要的是,在通過繼承實現路徑的時候,會造成雙方耦合性增大,而這又進一步加劇了擴展的復雜性。使用橋結構模式可以很好地規避這些問題:重在解耦。
- 中杯加奶中杯不加奶大杯加奶大杯不加奶