iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0

今天我們要講的是 Bridge 模式。如同 DAY10, DAY11 的 Strategy 模式,我會分做兩篇:案例討論與應用 Bridge 模式以及實際探討何謂 Bridge 模式

例子:來畫圖

這個例子是這樣的:我們需要要撰寫一個程式,它會使用既存的兩種繪圖程式(DP1DP2)之一繪製矩形。當實體化矩形後,它會知道該使用哪種繪圖程式。

可能解決的方案

先講第一種可能的方式。因為需求說到「實體化矩形後,它會知道該使用哪種繪圖程式」,所以我們可以有兩種不同的矩形物件:一種使用 DP1、一種使用 DP2。兩種矩形都有自己的繪製方法,但實作方式不同。如圖所示。

新的需求

當然,作為第一個被提出的可能解法,它本身一定是個有問題的解法。

假設今天新增了圓形,它如同矩形一樣需要透過這兩種繪圖程式進行繪製。同時,我們也要求在繪製過程中,我們無須知道 RectangleCircle 的差異。

此需求的實作方式其實也很容易,我們可以在增加一層類別,名為 Shape,而 RectangleCircle 都是 Shape 的一種。往下,Circle 也做與 Rectangle 一樣的結構設計。最後成果如下圖。

存在一些問題

這個方案是個滿直覺的解決方案。當然,它有一些問題存在。我們關注一下這個類別層次中的第三層(V1RectangleV2Rectangle 等)。如果⋯⋯

  • 今天冒出了一個 DP3,這樣就會從 4 個變做 6 個不同類型的 shape
  • 今天再多出一個 Shape,這樣就會有 9 種 Shape

於是類別就會需求的變化有爆炸性的增長。這個現象的原因是因為這個方案中的抽象(Shape 的種類)與實作(繪圖程式)是緊耦合的關係。

這會使我們的程式不易維護。

另一種替代方案

第一種方案是由於錯誤的繼承層次造成,那麼,用不同的層次呢?舉例來說,不用形狀的差異來把 Shape 來做分類,反而是用使用何種繪圖程式來分類。那麼,實踐出來的 UML 圖會如下。

但如此做也沒有好到哪裡去,類別的爆炸性增長並沒有因此解決。

實踐 Bridge 模式的方案

現在,我們打算引入設計模式到這個問題上,並嘗試解決它。

把學到的原則用於推導解決方案上

在劈頭就直接說明 Bridge 模式,並二話不說地拿來套用在上述案例前,不如先試著回想前幾天提到的一些原則,再來看看套用在這個案例上,會如何實踐。

我們會想到基本的兩條策略:

  • 找出變化並且將其封裝
  • 與其使用類別繼承,不如採用物件聚合的方式

變化之處,將其封裝

用共通性與可變性分析:用在這案例中,共同的概念會是「形狀」與「繪圖程式」;在變化的則是形狀的種類以及繪圖程式的種類。

共同的部分抽做抽象類別,再將變化的部分封裝成衍生類別繼承於其之下。這麼一來,我們就有下圖的兩個類別層次。

注意到 V1DrawingV2Drawing 還尚未使用到 Dp1DP2,但我們總是可以拿前者去使用後者(Adapter 模式)。

使用聚合

再來,就讓其中一類使用另外一類(聚合)即可。這裡就有個有趣的問題產生:是要讓形狀使用繪圖程式,還是繪圖程式使用形狀呢?

我們會選用前者。但後者的問題在哪呢?

如果繪圖程式要直接繪製形狀,那麼它必須知道形狀是什麼,這既違反了「物件只對自己負責」的原則,也違反了封裝(類型的隱藏)。

而反觀前者,Shape 物件無須知道使用哪一種 Drawing 的類型,因為可以讓 Shape 參照 Drawing 類別。

最後,再將原本的 Dp1DP2 與我們的介面銜接上,我們就會得到我們的設計:

而這就是我們想要得到的 Bridge 模式!(上例中其實也包含了 adapter 模式)

至於 Bridge 模式的定義與關鍵特徵為何?我們下篇再繼續吧!


上一篇
Day11: Strategy 模式2
下一篇
DAY13: Bridge 模式2
系列文
來讀設計模式:Junior developer 跟大家一起練功22
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言