iT邦幫忙

2022 iThome 鐵人賽

DAY 10
1

讓我們沿用上一篇轉接頭的例子,在之前我們都一直是以「螢幕」作為一端去跟電腦做連線,假設現在我們把範圍擴大,將螢幕做分類,可能有不同規格類型的螢幕會需要跟電腦做連線,這時該怎麼辦呢?

聰明的你,如果有想到用昨天的「Strategy」模式去做變型,那真的是吸收相當快速啊~沒錯!我們讓 ScreenService 變成一個抽象類別,並且去引用 Adapter,這樣不同規格類型的螢幕只要都去繼承 ScreenService,然後在各自去做自己的實作,就成功了!

Bridge - 定義

將抽象與實作解耦合,使它們都可以獨立地變化。

https://ithelp.ithome.com.tw/upload/images/20220920/20136443tBwE1TetX0.png

(圖片來源:https://www.bogotobogo.com/DesignPatterns/images/bridge/bridgediagram.png)

範例 UML

這邊我做了以下幾點的改變

  • ScreenService 變成抽象類別,在建構子中去帶入 Adapter
  • 去除了 ScreenSwitchDic ,主要是因為在 Bridge模式中,他的概念比較偏向是當下才去做 Adapter的帶入。例如在昨天的範例中,我在 main 裡面第一段便是 string screenSpec = "AVG"; 我可以直接透過字典去找到對應規格,但在這個範例中,ScreenService screen_avg = new Screen_AVG(new HDMIAdapter()); 我是在建立一個 Screen_AVG 類別時就要一同決定好需要帶入什麼的 Adapter

https://ithelp.ithome.com.tw/upload/images/20220920/20136443PnqBcqD1iG.png

不囉嗦上Code!

using System;
using System.Collections.Generic;

namespace DAY10_Bridge
{
    class Program
    {
        static void Main(string[] args)
        {
            // 透過繼承自 ScreenService,可以實例化類別並做方法改寫,且在建構時先將要使用的 adapter 帶入
            ScreenService screen_avg = new Screen_AVG(new HDMIAdapter());
            screen_avg.UseAdapter();

            ScreenService screen_vga = new Screen_VGA(new USBAdapter());
            screen_vga.UseAdapter();

            ScreenService screen_dvi = new Screen_DVI(new DisplayPortAdapter());
            screen_dvi.UseAdapter();
        }
    }

    public abstract class ScreenService
    {
        protected Adapter _adapter;

        // 建構時便將 Adapter 帶入做聚合,讓之後要做繼承去實例化的類別可以直接使用 _adapter
        protected ScreenService(Adapter adapter)
        {
            this._adapter = adapter;
        }
        public abstract void UseAdapter();
    }

    public class Screen_AVG : ScreenService
    {
        public Screen_AVG(Adapter adapter) : base(adapter)
        {
        }
        public override void UseAdapter()
        {
            Console.WriteLine("我是Screen_AVG,我要使用轉接器做轉接囉!");
            _adapter.Switch(this.GetType().Name);
        }
    }

    public class Screen_VGA : ScreenService
    {
        public Screen_VGA(Adapter adapter) : base(adapter)
        {
        }
        public override void UseAdapter()
        {
            Console.WriteLine("我是Screen_VGA,我要使用轉接器做轉接囉!");
            _adapter.Switch(this.GetType().Name);
        }
    }

    public class Screen_DVI : ScreenService
    {
        public Screen_DVI(Adapter adapter) : base(adapter)
        {
        }
        public override void UseAdapter()
        {
            Console.WriteLine("我是Screen_DVI,我要使用轉接器做轉接囉!");
            _adapter.Switch(this.GetType().Name);
        }
    }

    public interface Adapter
    {
        void Switch(string screenSpec);
    }

    public class DisplayPortAdapter : Adapter
    {
        public void Switch(string screenSpec)
        {
            Console.WriteLine($"已將{screenSpec}轉換成DisplayPort!");
        }
    }

    public class USBAdapter : Adapter
    {
        public void Switch(string screenSpec)
        {
            Console.WriteLine($"已將{screenSpec}轉換成USB!");
        }
    }

    public class HDMIAdapter : Adapter
    {
        public void Switch(string screenSpec)
        {
            Console.WriteLine($"已將{screenSpec}轉換成HDMI!");
        }
    }
}
  • 結果

https://ithelp.ithome.com.tw/upload/images/20220920/20136443m4VthoaEAe.png

與 Strategy模式的差異

Strategy Bridge
目的 拆解實體類與 Behavior 的耦合性 拆解 Abstraction 和 Implementation 的耦合性
類型 物件行為 物件結構

簡單的小結

從今天的內容可以發現,Bridge模式遵循著兩個設計模式的大原則

  • 找出變化並封裝之。
  • 優先使用物件聚合,而不是類別繼承。

如果就定義上「將抽象與實作解耦合,使它們都可以獨立地變化」,看起來很矛盾,但以上面的例子來看,又不會覺得那麼奇怪了,抽象也就是讓我的螢幕能有很多種類,且都繼承自同個抽象類別,實作就是去實作抽象類別中定義的方法,並將這些實作都用介面去做約束,最後在抽象類別中去聚合介面,就達成抽象與實作解偶了,所以我的不同螢幕都可以去自由搭配所需的轉接器!

相向經過今天的介紹,是不是對設計模式越來越有感覺了,我們明天再繼續吧~


上一篇
【DAY9】Strategy模式 - 強化我的轉接器
下一篇
【DAY11】Factory模式 - 來當工廠老闆吧!
系列文
勇闖秘境!探索物件導向背後的設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言