iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0
Software Development

輕鬆學習設計模式Design Pattern系列 第 19

Day 19 橋接模式 Bridge Pattern

  • 分享至 

  • xImage
  •  

想像一下你家裡的電視遙控器,這個遙控器可以控制不同品牌的電視,遙控器本身的功能可能有:開機、關機、調整音量等,甚至隨著需求增加新的按鈕或功能。而電視的功能可能會隨著品牌或型號不同而有所變化。遙控器就像橋接這些不同電視的中介,讓使用者不必考慮每台電視內部的具體細節,只需要知道按下按鈕會有什麼效果。這樣的設計就是「橋接模式」的精髓:把「操作」與「具體實現」分開,讓兩者可以獨立發展。

什麼是橋接模式?

橋接模式是一種結構型設計模式,它的主要目的是將抽象部分與實現部分分離,使它們都可以獨立地變化。簡單來說這個模式可以讓你將「抽象層次」和「具體實作」分離開來,以便兩者可以各自演化而不必互相依賴。當你需要擴充系統時,無論是修改抽象部分還是實作部分,都能簡單且獨立進行。

在程式設計中,當你遇到一個類別因為實作過於複雜或多樣化而導致結構僵化時,你可以透過橋接模式將這些實作細節封裝到一個「實作介面」中,並讓抽象的類別持有該介面,從而達到靈活擴展的效果。橋接模式就像是在兩個獨立變化的維度之間搭建了一座橋樑。這座橋樑使得這兩個維度可以各自獨立地變化,而不會相互影響。

橋接模式在遊戲開發中的應用

橋接模式經常出現在需要靈活擴展功能的場景中。例如,在圖形繪製的應用中,我們可以有不同的形狀(比如圓形、方形),而每一種形狀可能需要用不同的方式呈現(比如螢幕顯示或列印)。在跨平台GUI系統中,不同作業系統有不同的實現方式。在資料庫程式中,可以切換不同的資料庫系統,對應到不同的資料庫介面,而不影響上層業務邏輯。

這邊我們以一個遊戲開發的例子來說明橋接模式。假設我們正在開發一個角色扮演遊戲,遊戲中有不同的角色(如戰士、法師)和不同的武器(如劍、法杖)。我們希望能夠自由地組合角色和武器,而不是為每種組合都建立一個新的類別。透過橋接模式,我們可以將「角色」與「武器」分開,未來當我們需要新增角色或武器方式時,就不必重新組合所有的類別,達到更好的擴展性。

這個模式的基本角色包括:

  1. 抽象部分(Abstraction):定義了抽象的介面。維護一個對實現者(Implementor)物件的引用。
  2. 精煉抽象(Refined Abstraction):擴展抽象類,可以新增更多的功能。
  3. 實現者(Implementor):定義實現類的介面,該介面不一定要與抽象類的介面完全一致。通常只提供基本操作,而抽象類定義的介面可能會做更多更複雜的操作。
  4. 具體實現(Concrete Implementor):實現實現者介面並提供具體實現。

以下是使用橋接模式實現這個需求的步驟:

首先我們定義武器的抽象介面,

// 抽象部分 Abstraction
class Weapon {
public:
    virtual void use() = 0;
    virtual ~Weapon() {}
};

然後我們實現具體的武器類別,

class Sword : public Weapon {
public:
    void use() override {
        std::cout << "揮舞劍攻擊" << std::endl;
    }
};

class Staff : public Weapon {
public:
    void use() override {
        std::cout << "使用法杖施法" << std::endl;
    }
};

接下來我們定義角色的抽象類別,

class Character {
protected:
    Weapon* weapon;
public:
    Character(Weapon* w) : weapon(w) {}
    virtual void fight() = 0;
    virtual ~Character() {}
};

最後我們實現具體的角色類別,

class Warrior : public Character {
public:
    Warrior(Weapon* w) : Character(w) {}
    void fight() override {
        std::cout << "戰士";
        weapon->use();
    }
};

class Mage : public Character {
public:
    Mage(Weapon* w) : Character(w) {}
    void fight() override {
        std::cout << "法師";
        weapon->use();
    }
};

我們可以在客戶端程式碼中自由組合角色和武器,

int main() {
    Weapon* sword = new Sword();
    Weapon* staff = new Staff();
    
    Character* warrior = new Warrior(sword);
    Character* mage = new Mage(staff);
    
    warrior->fight();  // 輸出: 戰士揮舞劍攻擊
    mage->fight();     // 輸出: 法師使用法杖施法
    
    // 甚至可以讓戰士使用法杖,或讓法師使用劍
    Character* warriorWithStaff = new Warrior(staff);
    Character* mageWithSword = new Mage(sword);
    
    warriorWithStaff->fight();  // 輸出: 戰士使用法杖施法
    mageWithSword->fight();     // 輸出: 法師揮舞劍攻擊
    
    // 釋放記憶體
    delete sword;
    delete staff;
    delete warrior;
    delete mage;
    delete warriorWithStaff;
    delete mageWithSword;
    
    return 0;
}

執行上述程式碼,我們會得到以下輸出:

戰士揮舞劍攻擊
法師使用法杖施法
戰士使用法杖施法
法師揮舞劍攻

在這個遊戲開發的例子中,就像是在角色(Character)和武器(Weapon)之間搭建一座「橋樑」。

橋的一端是抽象部分(Abstraction),這一端是由 Character 類別及其子類(Warrior 和 Mage)所組成。這代表了角色的抽象,定義了角色應該具有的行為(如 fight() 方法)。

橋的另一端是實現部分(Implementation),這一端是由 Weapon 介面及其具體實現(Sword 和 Staff)組成。這代表了武器的實現,定義了武器應該如何被使用(如 use() 方法)。

橋樑本身的核心是在 Character 類中的 Weapon* weapon 成員變數。這個成員變數將抽象部分(角色)和實現部分(武器)連接起來。

透過橋接模式,我們可以新增新的角色(如弓箭手)或新的武器(如弓箭),而不需要修改現有的程式碼,實現了角色和武器可以獨立變化。任何角色都可以使用任何武器,這種組合是在執行時決定的,而不是在編譯時。透過橋接模式還能減少類別數量,如果不使用橋接模式,我們可能需要為每種角色與武器組合建立一個類別(如 WarriorWithSword, MageWithStaff 等),這會導致類別數量的急劇增加。

所以這個橋接模式實際上是一種靈活的連接機制,它允許角色和武器這兩個維度獨立變化,同時又能靈活地組合在一起。這就是橋接模式的核心思想:將抽象部分與其實現部分分離,使它們都可以獨立地變化。

橋接模式的優缺點

橋接模式最大的優點在於它讓抽象與實作分離,因此可以靈活地新增或修改兩者中的任意一方,而不會影響到另一方。這種鬆耦合的設計使得系統的擴展性大大提高,特別是在面對複雜的物件結構或多樣化需求時,它能有效地減少程式碼重複,並且讓系統維護變得更加容易。

橋接模式也並非在所有情況下都是最佳選擇。當你的系統結構簡單、需求較為固定時,這種設計可能會增加不必要的複雜度。另外,由於引入了額外的抽象層,開發初期可能會需要更多的規劃和設計,這在某些情況下會稍微增加開發的難度。

總結

橋接模式特別適合那些需要同時處理多個維度變化的系統。它讓抽象和實作分離,使得系統可以更靈活地演進。在現實世界中,當你面對多重需求的擴展時,橋接模式能讓你輕鬆應對各種變化,避免系統變得難以維護或過於複雜。透過合理的使用橋接模式,你可以在保持系統簡潔的同時,獲得更大的靈活性與可擴展性。

更多C++語言相關的文章,歡迎追蹤我的部落格。
https://shengyu7697.github.io/cpp-bridge-pattern/


上一篇
Day 18 責任鏈模式 Chain of Responsibility Pattern
系列文
輕鬆學習設計模式Design Pattern19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言