1. 程式人生 > 實用技巧 >【設計模式】橋接模式

【設計模式】橋接模式

橋接模式

簡介

橋接模式將兩個獨立變化的維度設計成兩個獨立的繼承等級結構,而不會將兩者耦合在一起形成多層繼承結構,在抽象層將二者建立起一個抽象關聯,該關聯關係類似一座橋,將兩個獨立的等級結構連線起來。

橋接模式:將抽象部分與他的實現部分解耦,使得兩者都能獨立變化。

結構

實現

實現方式:

  • 明確類中獨立的維度。獨立的概念可能是:抽象/平臺,域/基礎設施,前端/後端,介面/實現。
  • 瞭解客戶端的業務需求,並在抽象基類中定義它們。
  • 確定在所有平臺上都可執行的業務。並在通用實現介面中宣告抽象部分所需的業務。
  • 為你域內的所有平臺建立實現類,但需確保它們遵循實現部分的介面。
  • 在抽象類中新增指向實現型別的引用成員變數。抽象部分會將大部分工作委派給該成員變數所指向的實現物件。
  • 如果你的高層邏輯有多個變體,則可通過拓展抽象積累為每一個變體建立一個精確抽象。
  • 客戶端程式碼必須將實現物件傳遞給抽象部分的建構函式才能使其能夠相互關聯。此後,客戶端只需與抽象物件進行互動,無需和實現物件打交道。
#include <iostream>
#include <string>

// 實現部分(實現類介面)
class Implementation {
public:
    virtual ~Implementation() {}
    virtual std::string OperationImplementation() const = 0;
};

// 具體實現
class ConcrateImplementationA : public Implementation {
public:
    std::string OperationImplementation() const override { 
        return "ConcreteImplementationA: 這是平臺A上的結果.\n";
    }
};

// 具體實現
class ConcrateImplementationB : public Implementation {
public:
    std::string OperationImplementation() const override { 
        return "ConcreteImplementationB: 這是平臺B上的結果.\n";
    }
};

// 抽象部分(抽象類)
class Abstraction {
protected:
    Implementation* implementation_;

public:
    Abstraction(Implementation* implementation) : implementation_(implementation) {}
    virtual ~Abstraction() {}
    virtual std::string Operation() const {
        return "Abstraction: Base operation with:\n" +
           this->implementation_->OperationImplementation();
    }
};

// 精確抽象(擴充套件抽象類)
class ExtendedAbstraction: public Abstraction {
public:
    ExtendedAbstraction(Implementation* implementation): Abstraction(implementation) {}
    std::string Operation() const override {
        return "ExtendedAbstraction: Extended operation with:\n" +
           this->implementation_->OperationImplementation();
    }
};

void ClientCode(const Abstraction& abstraction) {
    // ...
    std::cout << abstraction.Operation();
    // ...
}

int main(int argc, char *argv[]) {
    Implementation* implementation = new ConcrateImplementationA;
    Abstraction* abstraction = new Abstraction(implementation);
    ClientCode(*abstraction);
    std::cout << std::endl;
    delete implementation;
    delete abstraction;

    implementation = new ConcrateImplementationB;
    abstraction = new ExtendedAbstraction(implementation);
    ClientCode(*abstraction);
    delete implementation;
    delete abstraction;

    return 0;
}
# -*- coding: utf-8 -*-

from __future__ import annotations
from abc import ABC, abstractmethod


class Abstraction:
    """
    """

    def __init__(self, implementation: Implementation) -> None:
        self.implementation = implementation

    def operation(self) -> str:
        return (f"Abstraction: Base operation with:\n"
                f"{self.implementation.operation_implementation()}")


class ExtendedAbstraction(Abstraction):
    """
    """

    def operation(self) -> str:
        return (f"ExtendedAbstraction: Extended operation with:\n"
                f"{self.implementation.operation_implementation()}")


class Implementation(ABC):
    """
    """

    @abstractmethod
    def operation_implementation(self) -> str:
        pass


class ConcreteImplementationA(Implementation):
    def operation_implementation(self) -> str:
        return "ConcreteImplementationA: 這是平臺A上的結果."


class ConcreteImplementationB(Implementation):
    def operation_implementation(self) -> str:
        return "ConcreteImplementationB: 這是平臺B上的結果."


def client_code(abstraction: Abstraction) -> None:
    """
    """

    # ...

    print(abstraction.operation(), end="")

    # ...


if __name__ == "__main__":
    """
    """

    implementation = ConcreteImplementationA()
    abstraction = Abstraction(implementation)
    client_code(abstraction)

    print("\n")

    implementation = ConcreteImplementationB()
    abstraction = ExtendedAbstraction(implementation)
    client_code(abstraction)

例項

問題描述

我們知道新手機上能夠迅速安裝並運行遊戲,此時要求新增加一個遊戲時,能夠在已有手機上安裝並執行。

問題解答

分析知,手機是Abstraction,安裝並運行遊戲是Implementor,不同的手機是RefinedAbstraction,安裝並執行不同的遊戲是ConcreteImplementor

// Example.cppp

#include <iostream>


// 實現類介面
class Game {
public:
    virtual ~Game() {}
    virtual void play() const = 0;
};

// 具體實現類GmaeA
class GameA: public Game {
public:
    void play() const override {
        std::cout << "玩遊戲A" << std::endl;
    }
};

// 具體實現類GmaeB
class GameB: public Game {
public:
    void play() const override {
        std::cout << "玩遊戲B" << std::endl;
    }
};

// 抽象類Phone
class Phone {
private:
    Game *game;

public:
    virtual ~Phone() {}
    virtual void run(Game *game) = 0;
    virtual void play() const = 0;
};

// 擴充抽象類PhoneA
class PhoneA : public Phone {
private:
    Game *game;

public:
    void run(Game *game) override {
        this->game = game;
    }
    void play() const override {
        this->game->play();
    }
};


int main(int argc, char *argv[]) {
    Phone *phone = new PhoneA;

    Game *game = new GameA;
    phone->run(game);
    phone->play();
    delete game;

    game = new GameB;
    phone->run(game);
    phone->play();
    delete game;

    delete phone;

    return 0;
}

總結

優點

  • 你可以建立與平臺無關的類和程式。
  • 客戶端程式碼僅與高層抽象部分進行互動,不會接觸到平臺的詳細資訊。
  • 開閉原則。你可以新增抽象部分和實現部分,且它們之間不會相互影響。
  • 單一職責原則。抽象部分專注於處理高層邏輯,實現部分處理平臺細節。

缺點

  • 對高內聚的類使用該模式可能會讓程式碼更加複雜。

場景

  • 如果你想要拆分或重組一個具有多重功能的龐雜類(例如能與多個數據庫伺服器進行互動的類),可以使用該模式。
  • 如果你希望在幾個獨立維度上擴充套件一個類,可使用該模式。
  • 如果你需要在執行時切換不同實現方法,可使用該模式。

與其他模式的關係

  • 橋接模式通常會在開發前期進行設計,使你能夠將程式的各個部分獨立開來以便開發。另一方面,介面卡模式通常在已有程式中使用,讓相互不相容的類能很好地合作。
  • 橋接模式狀態模式策略模式(某種程式上包括介面卡模式)的介面都非常相似,實際上,它們都基於組合模式,即將工作委派給其他物件,不過也都各自解決了不同的問題。
  • 可以將抽象工廠模式橋接模式搭配使用。如果橋接模式定義的抽象只能與特定實現合作,這一對模式搭配將非常有用。在這種情況下,抽象工廠模式可以對這些關係進行封裝,並且對客戶端程式碼隱藏其複雜性。
  • 可以結合使用生成器(建造者)模式橋接模式,主管類負責抽象工作,各種不同的生成器負責實現工作。