橋接(Bridge)
[TOC]
定義
橋梁模式(Bridge Pattern)也叫做橋接模式,是一個(gè)比較簡單的模式,其定義如下:Decouple an abstraction from its implementation so that the two can vary independently.(將抽象和實(shí)現(xiàn)解耦,使得兩者可以獨(dú)立地變化。
)
這話的核心在于如何去解耦,抽象者引用實(shí)現(xiàn)者玩讳,需要更換能力只用更換實(shí)現(xiàn)者即可,就好像是Android中ListView的Adapter一樣嚼贡,set進(jìn)去的是由開發(fā)者實(shí)現(xiàn)的熏纯,如果想要更換邏輯,可以重新設(shè)置新的adapter即可粤策,listView通過對(duì)adapter的抽象方法樟澜,開發(fā)者內(nèi)聚自己的實(shí)現(xiàn)即可
要實(shí)現(xiàn)這樣一個(gè)設(shè)計(jì)模式,需要如下幾個(gè)步驟:
- Abstraction——抽象化角色:它的主要職責(zé)是定義出該角色的行為,同時(shí)保存一個(gè)對(duì)實(shí)現(xiàn)化角色的引用秩贰,該角色一般是抽象類
- Implementor——實(shí)現(xiàn)化角色:它是接口或者抽象類霹俺,定義角色必需的行為和屬性
- RefinedAbstraction——修正抽象化角色:它引用實(shí)現(xiàn)化角色對(duì)抽象化角色進(jìn)行修正
- ConcreteImplementor——具體實(shí)現(xiàn)化角色:它實(shí)現(xiàn)接口或抽象類定義的方法和屬性
書上的解釋給我的感覺很官方,有些晦澀毒费,下面我來談一談我對(duì)這個(gè)橋接設(shè)計(jì)模式的理解
根據(jù)設(shè)計(jì)原則丙唧,在進(jìn)行軟件設(shè)計(jì)的時(shí)候,我們需要盡量不變動(dòng)上層模塊觅玻,減少上層對(duì)底層模塊的耦合性想际,盡可能的內(nèi)聚模塊內(nèi)的功能,弱耦合溪厘,高內(nèi)聚的關(guān)系的結(jié)果其實(shí)就是對(duì)修改關(guān)閉胡本,對(duì)擴(kuò)展開放的原則,換成語言上的理解其實(shí)就是我們的業(yè)務(wù)流程畸悬,盡量直接使用抽象或者接口來完成業(yè)務(wù)工作侧甫,這樣業(yè)務(wù)上層模塊對(duì)底層業(yè)務(wù)實(shí)現(xiàn)的持有其實(shí)就是一個(gè)抽象類或者接口,那么當(dāng)我們?cè)跀U(kuò)展業(yè)務(wù)能力或者更換業(yè)務(wù)實(shí)現(xiàn)邏輯時(shí)候蹋宦,就可以做到很少改動(dòng)甚至不改動(dòng)的情況就完成的軟件的迭代和更新~
簡單場景使用
老聯(lián)盟了披粟,搞一把基本英雄的創(chuàng)建作為背景
- 英雄單位的基本類抽取抽象
- 技能抽象
- 英雄單位持有技能抽象單位,用以測試
coding
先聲明英雄單位的抽象類妆档,需要名字僻爽,類型和技能
public abstract class AbsHero {
private IHeroSkill mSkill;
private String mName;
private String mType;
public AbsHero(IHeroSkill skill, String name) {
mSkill = skill;
mName = name;
mType = genType();
}
protected abstract String genType();
public void test() {
System.out.println("開始測試英雄[" + mType + "-" + mName + "] 釋放技能Q:" + mSkill.releaseSkill(IHeroSkill.CMD.Q) + ",釋放技能W:" + mSkill.releaseSkill(IHeroSkill.CMD.W) + ",釋放技能E:" + mSkill.releaseSkill(IHeroSkill.CMD.E) + ",釋放技能R:" + mSkill.releaseSkill(IHeroSkill.CMD.R));
}
}
public interface IHeroSkill {
enum CMD {
Q, W, E, R
}
String releaseSkill(CMD cmd);
}
實(shí)現(xiàn)老亞索的技能創(chuàng)建和英雄子類
public class HappyMan extends AbsHero {
public HappyMan(IHeroSkill skill) {
super(skill, "亞索");
}
@Override
protected String genType() {
return "AD";
}
}
public class HappyManSkill implements IHeroSkill{
@Override
public String releaseSkill(CMD cmd) {
switch (cmd){
case E:
return "快樂就完事,問好也追不上我~";
case Q:
return "哈撒ki~";
case R:
return "Holy ye kei duo~";
case W:
return "風(fēng)墻~";
}
return "NOTHING";
}
}
進(jìn)行測試:
AbsHero happyMan = new HappyMan(new HappyManSkill());
happyMan.test();
//開始測試英雄[AD-亞索] 釋放技能Q:哈撒ki~,釋放技能W:風(fēng)墻~,釋放技能E:快樂就完事贾惦,問好也追不上我~,釋放技能R:Holy ye kei duo~
可以看到我們的上層模塊對(duì)英雄的持有是抽象的,對(duì)于技能的實(shí)現(xiàn)也是通過依賴注入的方式傳遞給了英雄中敦捧,然后直接調(diào)用測試~
接下來我們創(chuàng)建個(gè)新英雄狗頭~
public class DogMan extends AbsHero {
public DogMan(IHeroSkill skill) {
super(skill, "狗頭");
}
@Override
protected String genType() {
return "AD-AP";
}
}
public class DogManSkill implements IHeroSkill {
@Override
public String releaseSkill(CMD cmd) {
switch (cmd){
case E:
return "畫個(gè)圈圈詛咒你~";
case Q:
return "專注發(fā)育疊被動(dòng)~";
case R:
return "變大须板,增強(qiáng)自身屬性";
case W:
return "惡心對(duì)面,降低對(duì)面雙抗兢卵,減速~";
}
return "NOTHING";
}
}
AbsHero happyMan = new HappyMan(new HappyManSkill());
happyMan.test();
AbsHero goutou = new DogMan(new DogManSkill());
goutou.test();
//開始測試英雄[AD-亞索] 釋放技能Q:哈撒ki~,釋放技能W:風(fēng)墻~,釋放技能E:快樂就完事习瑰,問好也追不上我~,釋放技能R:Holy ye kei duo~
//開始測試英雄[AD-AP-狗頭] 釋放技能Q:專注發(fā)育疊被動(dòng)~,釋放技能W:惡心對(duì)面,降低對(duì)面雙抗秽荤,減速~,釋放技能E:畫個(gè)圈圈詛咒你~,釋放技能R:變大甜奄,增強(qiáng)自身屬性
可以看到很方便我們就能創(chuàng)建第二英雄,并且技能可以隨便配置窃款,如果想要削弱某個(gè)英雄或者重做某個(gè)英雄的技能课兄,直接更換已有的技能實(shí)現(xiàn)即可,我們的技能作為接口的方式給英雄來依賴晨继,既不會(huì)侵入后期技能創(chuàng)建的迭代烟阐,也不會(huì)影響今后技能的生產(chǎn),唯一可能帶來的問題就可能是接口方法會(huì)發(fā)生改動(dòng),這樣會(huì)直接導(dǎo)致實(shí)現(xiàn)者要發(fā)生變化蜒茄,其實(shí)就算如此也不必慌唉擂,這是語言特性所導(dǎo)致,我們完全可以通過一個(gè)抽象類來實(shí)現(xiàn)這個(gè)接口檀葛,默認(rèn)其他的技能創(chuàng)建者也都通過繼承這個(gè)抽象類即可玩祟,這樣就避免了今后就算接口改動(dòng)導(dǎo)致其他子類需要改動(dòng)的成本~
特點(diǎn)
這里還是把書里總結(jié)的貼來哈~
- 抽象和實(shí)現(xiàn)分離:這也是橋梁模式的主要特點(diǎn),它完全是為了解決繼承的缺點(diǎn)而提出的設(shè)計(jì)模式屿聋。在該模式下空扎,實(shí)現(xiàn)可以不受抽象的約束,不用再綁定在一個(gè)固定的抽象層次上
- 優(yōu)秀的擴(kuò)充能力:看看我們的例子胜臊,想增加實(shí)現(xiàn)勺卢?沒問題!想增加抽象象对,也沒有問題黑忱!只要對(duì)外暴露的接口層允許這樣的變化,我們已經(jīng)把變化的可能性減到最小
- 實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶透明:客戶不用關(guān)心細(xì)節(jié)的實(shí)現(xiàn)勒魔,它已經(jīng)由抽象層通過聚合關(guān)系完成了封裝
使用場景
- 不希望或不適用使用繼承的場景:例如繼承層次過渡甫煞、無法更細(xì)化設(shè)計(jì)顆粒等場景,需要考慮使用橋梁模式
- 接口或抽象類不穩(wěn)定的場景:明知道接口不穩(wěn)定還想通過實(shí)現(xiàn)或繼承來實(shí)現(xiàn)業(yè)務(wù)需求冠绢,那是得不償失的抚吠,也是比較失敗的做法
- 重用性要求較高的場景:設(shè)計(jì)的顆粒度越細(xì),則被重用的可能性就越大弟胀,而采用繼承則受父類的限制楷力,不可能出現(xiàn)太細(xì)的顆粒度(
這里需要補(bǔ)充一點(diǎn),粒度的粗細(xì)由開發(fā)者自己掌握孵户,不是越細(xì)越好萧朝,越細(xì)帶來的開發(fā)成本也會(huì)相對(duì)上升,所以對(duì)于粒度的掌握還是要根據(jù)項(xiàng)目的規(guī)模和個(gè)人的經(jīng)驗(yàn)來把握
)
總結(jié)
橋梁模式是非常簡單的夏哭,使用該模式時(shí)主要考慮如何拆分抽象和實(shí)現(xiàn)检柬,并不是一涉及繼承就要考慮使用該模式,那還要繼承干什么呢竖配?橋梁模式的意圖還是對(duì)變化的封裝何址,盡量把可能變化的因素封裝到最細(xì)、最小的邏輯單元中进胯,避免風(fēng)險(xiǎn)擴(kuò)散用爪。因此讀者在進(jìn)行系統(tǒng)設(shè)計(jì)時(shí),發(fā)現(xiàn)類的繼承有N層時(shí)龄减,可以考慮使用橋梁模式项钮。`認(rèn)為是系統(tǒng)中最容易重現(xiàn)變化的環(huán)節(jié)以接口的方式進(jìn)行依賴,粒度越細(xì)越好,粒度的把握參閱上述