設(shè)計模式系列教程—Bridge Pattern(橋接模式)

15 Bridge Pattern(橋接模式)

前言:把實現(xiàn)分開,讓它們各自變化
需求:
麥當(dāng)當(dāng)和肯打基是Vander小時候的最愛,Vander發(fā)達(dá)之后,他也想加盟它們?nèi)シ忠槐F(xiàn)在合同已經(jīng)談攏了,Vander開始加盟它們制作食物,他很快就開始了設(shè)計,設(shè)計如下:

Restaurant:

public abstract class Restaurant {

    protected abstract void makeFood();
    
}

Kentucky:

public abstract class Kentucky extends Restaurant {

}

MacDonald:

public abstract class MacDonald extends Restaurant {

}

KentuckyFriedChickenFactory:

public class KentuckyFriedChickenFactory extends Kentucky  {
        // 獲取父類的名字(super.getClass().getSimpleName()依然還是獲取當(dāng)前類的名字)
    private final String RESTAURANT_NAME = this.getClass().getSuperclass().getSimpleName();
    
    public void makeFood() {
        System.out.println("生產(chǎn)" + RESTAURANT_NAME + "的炸雞");
    }
    
}

MacDonaldFriedChickenFactory:

public class MacDonaldFriedChickenFactory extends MacDonald  {

    private final String RESTAURANT_NAME = super.getClass().getSuperclass().getSimpleName();
    
    public void makeFood() {
        System.out.println("生產(chǎn)" + RESTAURANT_NAME + "的炸雞");
    }
    
}

KentuckyHamburgFactory:

public class KentuckyHamburgFactory extends Kentucky  {

    private final String RESTAURANT_NAME = this.getClass().getSuperclass().getSimpleName();
    
    public void makeFood() {
        System.out.println("生產(chǎn)" + RESTAURANT_NAME + "的漢堡");
    }
    
}

MacDonaldHamburgFactory:

public class MacDonaldHamburgFactory extends MacDonald  {

    private final String RESTAURANT_NAME = this.getClass().getSuperclass().getSimpleName();
    
    public void makeFood() {
        System.out.println("生產(chǎn)" + RESTAURANT_NAME + "的炸雞");
    }
    
}

首先肯打雞跟麥當(dāng)當(dāng)都是Restaurant,很理所當(dāng)然地實現(xiàn)Restaurant接口,肯打雞生產(chǎn)漢堡的工廠跟生產(chǎn)炸雞的工廠都繼承Kentucky,類似的麥當(dāng)當(dāng)也是類似的情況,接下來Vander聽說漢堡仔的漢堡跟薯條都非常好吃,所以也想加盟其中,然后又在Restaurant基礎(chǔ)上繼續(xù)加入,并且需要添加薯條這種食物,接著三家餐廳都得加入炸薯條工廠生產(chǎn)炸薯條,這太累了,Vander想有沒有更好的方法呢。

此時Panda大師又來了,她一看就覺得這么設(shè)計其實有個很大的問題。

Panda:你這樣的設(shè)計用了繼承來實現(xiàn),但是繼承的關(guān)系在編譯時就定義好了,所以沒法在運行的時候改變父類繼承的實現(xiàn),意思就是說你沒法在運行的時候指定用哪個工廠生產(chǎn)哪種種類的食物。這里就涉及到以前的一個原則,合成/聚合復(fù)用

原則:盡量使用合成/聚合,盡量不要使用類繼承去實現(xiàn)。
Vander:具體應(yīng)該怎么操作呢?不明白合成跟聚合是什么,能不能舉個例子
Panda:請看以下的類圖,馬聚合成馬群(聚合),而四肢是屬于馬的一部分(合成)

Panda:使用這個原則的好處就是,優(yōu)先使用對象的合成/聚合將有助于你保持每個類被封裝,并被集中在單個任務(wù)上,這樣累和類繼承層次會保持較小規(guī)模,并且不太可能增長成為不可控制的龐然大物。說了這么多你可以試試以下的設(shè)計:

Restaurant:

public abstract class Restaurant {

    private FoodFactory foodFactory;
    
    protected void makeFood() {
        foodFactory.makeFood();
    }
    
    public void setFoodFactory(FoodFactory foodFactory) {
        this.foodFactory = foodFactory;
    }
    
}

MacDonald:

public class MacDonald extends Restaurant {

    private final String RESTAURANT_NAME = this.getClass().getSimpleName();
    
    public MacDonald() {
        System.out.println(RESTAURANT_NAME + ":");
    }
    
}

Kentucky:

public class Kentucky extends Restaurant {
    
    private final String RESTAURANT_NAME = this.getClass().getSimpleName();
    
    public Kentucky() {
        System.out.println(RESTAURANT_NAME + ":");
    }
    
}

FoodFactory:

public abstract class FoodFactory {

    public abstract void makeFood();
    
}

FriedChickenFactory:

public class FriedChickenFactory extends FoodFactory  {

    public void makeFood() {
        System.out.println("    生產(chǎn)炸雞");
    }
    
}

HamburgFactory:

public class HamburgFactory extends FoodFactory  {

    public void makeFood() {
        System.out.println("    生產(chǎn)漢堡");
    }
    
}

OrderFood:

public class OrderFood {

    public static void main(String[] args) {
        Restaurant restaurant;
        restaurant = new MacDonald();
        restaurant.setFoodFactory(new FriedChickenFactory());
        restaurant.makeFood();
        restaurant.setFoodFactory(new HamburgFactory());
        restaurant.makeFood();
        restaurant = new Kentucky();
        restaurant.setFoodFactory(new FriedChickenFactory());
        restaurant.makeFood();
        restaurant.setFoodFactory(new HamburgFactory());
        restaurant.makeFood();
    }

}

實現(xiàn)效果:

現(xiàn)在這么設(shè)計有個好處,就是如果要加盟一個新的餐廳,只需要實現(xiàn)多一個Restaurant接口類,如果要添加多一種食物,只需要實現(xiàn)多一個對應(yīng)的食物的工廠就完事了,事實上是利用了前面學(xué)到的“單一責(zé)任原則”,讓創(chuàng)建食物的工作交給了食物工廠,然后通過Restaurant來指定是哪個餐廳的食物,而不是原來那樣將哪個餐廳的哪種食物都糅合在一起了,相當(dāng)于本來的食物工廠又加入了餐廳的元素,導(dǎo)致了一個類有兩種變化的可能。

下面給橋接模式下個定義:

橋接模式:將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化。

簡單說明一下這里的抽象部分和實現(xiàn)部分,并不是說抽象類跟實現(xiàn)類的分離,而是說實現(xiàn)的部分就是變化的原因,其實就是將變化的原因獨立出來,讓它們各自去變化,不互相影響。也就是上面的例子中,餐廳的變化跟食物的變化要獨立開來。

image.png

最后又到了喜聞樂見的總結(jié)部分,我們又來總結(jié)我們現(xiàn)在現(xiàn)有的設(shè)計模式武器。

面向?qū)ο蠡A(chǔ)

抽象、封裝、多態(tài)、繼承

九大設(shè)計原則

設(shè)計原則一:封裝變化
設(shè)計原則二:針對接口編程,不針對實現(xiàn)編程
設(shè)計原則三:多用組合,少用繼承
設(shè)計原則四:為交互對象之間的松耦合設(shè)計而努力
設(shè)計原則五:對擴(kuò)展開放,對修改關(guān)閉
設(shè)計原則六:依賴抽象,不要依賴于具體的類
設(shè)計原則七:只和你的密友談話
設(shè)計原則八:別找我,我有需要會找你
設(shè)計原則九:類應(yīng)該只有一個改變的理由

模式

橋接模式:將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容