前言
人在IT江湖飄,不懂設計模式咋裝X?
橋接模式在日常開發(fā)中不是特別常用,主要是因為上手難度較大签则,但是對于理解面向?qū)ο笤O計有非常大的幫助。
定義
橋接模式是將抽象部分與它的實現(xiàn)部分分離铐料,使它們都可以獨立地變化渐裂。它是一種對象結(jié)構(gòu)型模式,又稱為柄體(Handle and Body)模式或接口(Interfce)模式钠惩。
使用場景
橋我們大家都熟悉柒凉,顧名思義就是用來將河的兩岸聯(lián)系起來的。而此處的橋是用來將兩個獨立的結(jié)構(gòu)聯(lián)系起來篓跛,而這兩個被聯(lián)系起來的結(jié)構(gòu)可以獨立的變化膝捞,所有其他的理解只要建立在這個層面上就會比較容易。
下面是一些官方的說明愧沟,比較晦澀蔬咬,必須等你有一定的經(jīng)驗后才能理解: 1. 如果一個系統(tǒng)需要在抽象化和具體化之間增加更多的靈活性,避免在兩個層次之間建立靜態(tài)的繼承關系沐寺,通過橋接模式可以使它們在抽象層建立一個關聯(lián)關系林艘。
“抽象部分”和“實現(xiàn)部分”可以以繼承的方式獨立擴展而互不影響,在程序運行時可以動態(tài)將一個抽象化子類的對象和一個實現(xiàn)化子類的對象進行組合混坞,即系統(tǒng)需要對抽象化角色和實現(xiàn)化角色進行動態(tài)耦合狐援。
一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都需要獨立進行擴展究孕。
對于那些不希望使用繼承或因為多層繼承導致系統(tǒng)類的個數(shù)急劇增加的系統(tǒng)啥酱,橋接模式尤為適用。
如何實現(xiàn)
業(yè)務場景
在講策略模式的時候蚊俺,王二狗和牛翠花不是要到天津之眼去約炮懈涛,不,約會嘛泳猬,兩人到那后先去星巴克喝咖啡了,星巴克提供了多種選擇宇植,從容量上說有大杯得封,中杯,小杯指郁,從口味上說有原味忙上,加糖,這可難為了有選擇恐懼癥的牛翠花闲坎,半天點不出來疫粥,后面的人都開始罵娘了茬斧,王二狗也只能陪著笑臉道歉。 其實被難為的除了牛翠花梗逮,還有給星巴克做訂單系統(tǒng)的外包公司的程序員林蛋大项秉。一開始提需求的時候星巴克說我們只有正常杯(中杯),原味和加糖這幾種選擇慷彤,人家林蛋大也是有兩年工作經(jīng)驗的碼農(nóng)娄蔼,這需求不在話下,蛋大還想到了要面對抽象編程底哗。
首先定義一個點咖啡接口岁诉,里面有一個下單方法,至于點哪種口味的咖啡跋选,就由其子類去決定涕癣,完美,蛋大好牛逼扒氨辍属划!
public interface ICoffee {
void orderCoffee(int count);
}
原味咖啡類
public class CoffeeOriginal implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("原味咖啡%d杯",count));
}
}
加糖咖啡類
public class CoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("加糖咖啡%d杯",count));
}
}
搞定收工,王者榮耀搞起候生!項目經(jīng)理突然過來了:蛋大啊同眯,客戶那邊說了,他們準備加兩個容量規(guī)格的咖啡唯鸭,大杯和小杯须蜗,你改一下。林蛋大:what目溉?尼瑪為什么不早說明肮,老子剛寫完。胸中雖有萬千牢騷缭付,還不得平復一下心情去改代碼柿估,何必呢?蛋大心中暗喜陷猫,幸好老子是面向抽象編程的秫舌,對應加幾個實現(xiàn)類不就得了?
//中杯加糖
public class CoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("中杯加糖咖啡%d杯",count));
}
}
//大杯加糖
public class LargeCoffeeWithSugar implements ICoffee {
@Override
public void orderCoffee(int count) {
System.out.println(String.format("大杯加糖咖啡%d杯",count));
}
}
绣檬。足陨。。
加著加著蛋大慌了娇未,共需要3x2=6個類啊墨缘,大杯原味和加糖,中杯原味和加糖,小杯原味和加糖镊讼。過段時間萬一那二筆客戶又要出加奶宽涌,加蜂蜜等等口味,說不定還有迷你杯蝶棋,女神杯等等規(guī)格的咖啡卸亮,那我這邊的類不就爆炸了嗎?看來的去找個設計模式了嚼松。嫡良。。
此場景橋接模式正合適献酗,這里有兩個變化維度寝受,咖啡的容量和口味,而且都需要獨立變化罕偎。如果使用繼承的方式很澄,隨著變化類就會急劇的增加。你可以將容量理解為抽象部分颜及,而口味理解為實現(xiàn)部分甩苛,這兩個部分需要橋接。
使用橋接模式
橋接模式UML 圖如下
橋梁模式所涉及的角色有:
- 抽象化(Abstraction)角色:抽象化給出的定義俏站,并保存一個對實現(xiàn)化對象的引用讯蒲。
- 修正抽象化(RefinedAbstraction)角色:擴展抽象化角色,改變和修正父類對抽象化的定義肄扎。
- 實現(xiàn)化(Implementor)角色:這個角色給出實現(xiàn)化角色的接口墨林,但不給出具體的實現(xiàn)。必須指出的是犯祠,這個接口不一定和抽象化角色的接口定義相同旭等,實際上,這兩個接口可以非常不一樣衡载。實現(xiàn)化角色應當只給出底層操作搔耕,而抽象化角色應當只給出基于底層操作的更高一層的操作。
- 具體實現(xiàn)化(ConcreteImplementor)角色:這個角色給出實現(xiàn)化角色接口的具體實現(xiàn)痰娱。
抽象化角色就像是一個水杯的手柄弃榨,而實現(xiàn)化角色和具體實現(xiàn)化角色就像是水杯的杯身。手柄控制杯身猜揪,這就是此模式別名“柄體”的來源惭墓。
林蛋大查了半天不知道使用哪個設計模式,只能求助前輩王二狗了而姐。電話通了:狗哥,我這有個問題划咐。拴念。钧萍。王二狗:我這正解決終身大事呢,沒工夫理你政鼠,你去調(diào)查一下橋接模式风瘦。林蛋大:不愧是大神,改天請你和嫂子吃飯公般,先掛啦万搔。掛電話后,蛋大就開始用橋接模式重構(gòu)代碼了官帘。
蛋大分析了當前業(yè)務場景瞬雹,認為可以將咖啡的容量作為抽象化Abstraction,而咖啡口味為實現(xiàn)化Implementor
第一步:創(chuàng)建抽象化部分:
//抽象化Abstraction
public abstract class Coffee {
protected ICoffeeAdditives additives;
public Coffee(ICoffeeAdditives additives){
this.additives=additives;
}
public abstract void orderCoffee(int count);
}
我們可以看到刽虹,Coffee
持有了ICoffeeAdditives
引用酗捌,ICoffeeAdditives
的實例是通過構(gòu)造函數(shù)注入的,這個過程就是我們所說的橋接過程涌哲。我們通過這個引用就可以調(diào)用ICoffeeAdditives
的方法胖缤,進而將Coffee
的行為與ICoffeeAdditives
的行為通過orderCoffee()
方法而組合起來。
下面是一個對抽象化修正的一個類,里面增加了一個品控的方法
//RefinedAbstraction
public abstract class RefinedCoffee extends Coffee {
public RefinedCoffee(ICoffeeAdditives additives) {
super(additives);
}
public void checkQuality(){
Random ran=new Random();
System.out.println(String.format("%s 添加%s",additives.getClass().getSimpleName(),ran.nextBoolean()?"太多":"正常"));
}
}
第二步:創(chuàng)建實現(xiàn)化部分
public interface ICoffeeAdditives {
void addSomething();
}
//加奶
public class Milk implements ICoffeeAdditives {
@Override
public void addSomething() {
System.out.println("加奶");
}
}
//加糖
public class Sugar implements ICoffeeAdditives {
@Override
public void addSomething() {
System.out.println("加糖");
}
}
第三步:客戶端調(diào)用
public static void main(String[] args) {
//點兩杯加奶的大杯咖啡
RefinedCoffee largeWithMilk=new LargeCoffee(new Milk());
largeWithMilk.orderCoffee(2);
largeWithMilk.checkQuality();
}
輸出結(jié)果:
加奶
大杯咖啡2杯
Milk 添加太多
通過使用橋接模式阀圾,就使得咖啡的容量和口味這兩個維度可以獨立變化哪廓,互不干擾。林蛋大終于完成了代碼的重構(gòu)初烘,回頭一看王者榮耀由于惡意掛機被扣除15個信用分涡真,暫時不能進行排位賽,無奈只能再去鞏固一下橋接模式了账月。
優(yōu)缺點
優(yōu)點:
- 分離抽象接口及其實現(xiàn)部分综膀。橋接模式使用“對象間的關聯(lián)關系”解耦了抽象和實現(xiàn)之間固有的綁定關系,使得抽象和實現(xiàn)可以沿著各自的維度來變化局齿。所謂抽象和實現(xiàn)沿著各自維度的變化剧劝,也就是說抽象和實現(xiàn)不再在同一個繼承層次結(jié)構(gòu)中,而是“子類化”它們抓歼,使它們各自都具有自己的子類讥此,以便任何組合子類,從而獲得多維度組合對象谣妻。
- 在很多情況下萄喳,橋接模式可以取代多層繼承方案,多層繼承方案違背了“單一職責原則”蹋半,復用性較差他巨,且類的個數(shù)非常多,橋接模式是比多層繼承方案更好的解決方法,它極大減少了子類的個數(shù)染突。
- 橋接模式提高了系統(tǒng)的可擴展性捻爷,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統(tǒng)份企,符合“開閉原則”也榄。
缺點:
- 橋接模式的使用會增加系統(tǒng)的理解與設計難度,由于關聯(lián)關系建立在抽象層司志,要求開發(fā)者一開始就針對抽象層進行設計與編程甜紫。
- 橋接模式要求正確識別出系統(tǒng)中兩個獨立變化的維度,因此其使用范圍具有一定的局限性骂远,如何正確識別兩個獨立維度也需要一定的經(jīng)驗積累囚霸。