譯-設計模式-結構模式之Bridge

更多請移步: 我的博客

目的

Bridge是結構模式的一種,它可以幫你分離一個巨大的類或者將一組關係相近的類分離成爲兩個獨立的層次結構,抽象和實現,可以各自獨立開發。

問題

抽象?實現?聽起來害怕?我們首先來看個簡單的例子。

你有一個幾何Shap類,他有一對子類:Square和Triangle。你希望擴展這個層次結構來融入顏色以便創建紅色和藍色的形狀。但是因爲你已經有子類,你需要創建4個類來組合,比如BlueSquare和RedTriangle。

shapeCombination

添加更多的形狀類型和顏色將會導致層級結構變的更大。比如,添加一個Circles,你必須創建兩個子類,每個對應一種顏色。之後,再添加新的顏色就需要爲每個形狀添加一個子類。再往後發展,將會變的更糟。

解決

我們每次在擴展類層次到幾個獨立緯度時都會碰到這個問題。

Bridge模式嘗試通過用委託替換繼承來解決這個問題。你必須將其中一個維度抽離到不同的層次結構中。原類將通過持有新層次結構中的一個對象的引用的方式來替換在一個類中保留它所有的狀態和行爲的方式。

shapeSolution

通過這種方式我們抽離出Color和它的兩個子類,Red和Blue。Shape類將持有一個顏色類的引用域。當我們需要時通過這個引用把工作委託給color對象。這個引用在Shapps和Color之間像橋一樣提供服務。從現在開始,添加color將不再需要改變shape類,反之亦然。

抽象(Abstraction)和實現(Implementation)

在GoF的書中把“抽象和實現”一詞作爲Bridge模式定義的一部分。在我看來,那樣太過學術並且使得這個模式變的更加難以理解。通過上面那個簡單的例子,讓我們看下GoF的真正意思。

抽象(Abstraction),也叫做接口(Interface),是一些實體的控制層。它們自己並不做任何真正事情,而是把大多工作委託給實現(Implementation)層,有時叫做平臺(Platform)。不要把接口和抽象類和你的編程語言混淆,他們不是一回事。

比如,當我們談到真正的應用是,抽象可以表示用戶圖形界面(GUI),實現可以表示響應用戶交互的GUI層調用系統底層操作的API。

有兩個方向來將應用擴展:
- 有一些不同的GUI(用戶GUI和管理GUI)
- 支持一些不同的API(可以工作在Windows,Linux和MacOS下)

這個程序的代碼看起來像“巨大的意大利麪條碗”,有着成噸的連接不同GUI和API行爲的操作條件。

compare

可以通過對所有接口-平臺的變體進行子類化來改進代碼。但實際上,這個將導致我們已經在形狀例子中看到的同樣的問題。類層次將爆發時增長,每個新GUI或者API類型將需要增加一些組合類。

Bridge模式建議把這些類分成兩個層次:
- 抽象層(Abstraction):應用的GUI層。
- 實現層(Implementation):操作系統API。

抽象對象持有一個具體實現對象的引用。只要遵循通用的接口,使同一個GUI能夠在Windows和Linux下工作,不同的實現將是可互換的。

exampleApp

更重要的是,你可以在不觸碰操作系統代碼的情況下開始在GUI類中工作,反之亦然。比如,添加一個對新操作系統的支持,將僅需要在實現層中創建一個子類。

結構

structure

  1. 抽象(Abstraction)主要包含一個像用戶接口一樣的控制邏輯。抽象代碼依賴具體實現對象來完成任務。

  2. 實現(Implementation)爲所有具體的實現聲明瞭通用的接口。抽象層可以和任何一個符合這個接口的具體實現工作。
    抽象和實現接口在一些程序中是相等的。但是大多情況下,實現包含基本的原語操作,抽象層用它們來處理一些複雜的行爲。

  3. 具體實現包含具體平臺的代碼。

  4. 精製抽象(Refined Abstractions)可以用來創建一些控制邏輯的變種。這些類和它們的父類一樣,應該用實現接口(Implementation inteface)來和不同的實現協作。

  5. 客戶端(Client)只和抽象類有一個地方不同。在構建抽象對象時,客戶端會傳遞一個具體實現對象。然而,如果需要,具體實現可以動態替換。

僞代碼

在這個例子中,Bridge將設備(Devices)和遙控器(Remotes)的代碼分成幾部分:

  • 設備(看作實現)
  • 遙控器(看作抽象)

umlExample

遙控器的基類有一個域來持有一個要控制設備的對象引用。遙控器通過設備提供的通用接口工作。它允許一個遙控器可以和幾個不同的設備協作(譯者注:控制幾個不同類型的設備)。

你可以獨立的改變控制類。例如,你可以創建一個僅有兩個按鈕的遙控器或者帶有觸摸屏的複雜遙控器。

因此,Bridge模式允許你將一個實體分成幾個不同的實體,它們可以獨立發展。客戶端代碼總是保持簡單。它只需要選擇一個抽象並且配置給它一個具體實現。

// All remote classes contain reference to the device they controls. Remote's
// methods delegate most of the work to the device methods.
class Remote is
    protected field device: Device
    constructor BasicRemote(device: Device) is
        this.device = device
    method togglePower() is
        if device.isEnabled() then device.disable()
        else device.enable()
    method volumeDown() is
        device.setVolume(device.getVolume() - 10)
    method volumeUp() is
        device.setVolume(device.getVolume() + 10)
    method channelDown() is
        device.setChannel(device.getChannel() - 1)
    method channelUp() is
        device.setChannel(device.getChannel() + 1)

// You can extend remote hierarchy independently from device classes.
class AdvancedRemote extends BasicRemote is
    method mute() is
        device.setVolume(0)


// All devices have the common interface. This makes them compatible with
// all remotes.
interface Device is
    method isEnabled()
    method enable()
    method disable()
    method getVolume()
    method setVolume(percent)
    method getChannel()
    method setChannel(channel)

// But each concrete device may have its own implementation.
class Tv implements Device is
    // ...

class Radio implements Device is
    // ...


// Somewhere in client code.
tv = new Tv();
remote = new Remote(tv)
remote.pover()

radio = new Radio();
remote = new AdvancedRemote(radio)

適用性

  • 當你有一個包含一些功能變種的大類(比如,工作在幾個不同的數據庫服務上)
    這個類會變的很難維護,因爲任何一個人觸碰她的人都需要花費大量的時間去完全理解它。更改功能的某個變種會導致編輯整個類,這可能會引起討厭的被忽視的錯誤。

    Bridge模式將單體類分成幾個層次,一個包含另外一個的引用。這些層次中的類可以獨立的編輯。它簡化了支持,並最大限度地減少了修改現有代碼的風險。

  • 當你需要在正交(獨立)的維度擴展一個類
    取代單層次的增長,Bridge模式建議爲每個維度創建一個分離的類層次,並且通過引用域來關聯這些層次。

  • 當你需要在運行時改變實現
    儘管它是可選的,Bridge模式允許改變抽象中的實現對象。這就像爲一個字段分配一個新值一樣簡單。

    順便說下,這也是爲什麼許多人對Bridge模式和Strategy模式分不清楚。記住,模式不僅僅是類結構,而是意圖(譯者注:或者目的)。Bridge模式的目的就是結構化代碼。

如何實現

  1. 確定你的類是正交維度。這些獨立的概念可以是:abstraction/platform, or domain/infrastructure, or front-end/back-end, or interface/implementation.

  2. 考慮客戶端想要幹什麼,然後把他們描述在基本抽象類中。

  3. 確定所有平臺的能力和抽象需要什麼。然後把它們描述在實現接口中。

  4. 在你的領域中爲所有平臺創建具體實現類,確定它們都遵循了實現接口。

  5. 在抽象類中增加一個實現類型的域。然後實現所有抽象方法,同時將大部分工作委託給該域中引用的實現對象。

  6. 客戶端代碼應將實現對象傳遞給抽象的構造函數。它可以根據需要使用抽象對象。

優點

  • 允許建立平臺獨立代碼。
  • 符合開閉原則。
  • 對客戶端隱藏實現細節。

缺點

  • 創建多個附加類導致總體代碼複雜性增加。

和其他模式的關係

  • Bridge是前置設計,讓抽象和實現相互獨立。Adapter是通過改裝使得沒有關係的類協作。Adapter通常是在設計完成後使用;Bridge通常是設計時採用。

  • State, Strategy, Bridge (和某種程度上的Adapter) 具有相似的解決結構。它們都是採用分享“句柄/身體”的方式。它們的意圖不同,因此它們解決不同的問題。

  • Abstract Factory可與Bridge模式一起使用。當Bridge“接口”的一部分只能與特定的“實現”工作時,這很有用。在這種情況下,工廠可以封裝這些關係,對客戶端隱藏複雜性。

  • Builder可以構造爲Bridge模式:Director將作爲接口,Builders將扮演實現角色。

Java中模式的使用

用例:Bridge模式對這些情況特別適用:跨平臺應用,支持多類型的數據庫服務,和一些特定類型的API提供者(例如,雲平臺,社交網絡等)合作。

堅定:可以通過區分一些控制實體和它所依賴的幾個不同的平臺來識別Bridge模式。

參考

翻譯整理自:https://refactoring.guru/design-patterns/bridge

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章