2.2 橋接 Bridge

HUSTER593發表於2020-11-25

專業描述 橋接模式是一種結構型設計模式, 可將一個大類或一系列緊密相關的類拆分為抽象和實現兩個獨立的層次結構, 從而能在開發時分別使用。

可以這麼理解,在實際的程式中, 抽象部分是圖形使用者介面 (GUI),而實現部分則是底層作業系統程式碼 (API), GUI 層呼叫 API 層來對使用者的各種操作做出響應。

一般來說, 你可以在兩個獨立方向上擴充套件這種應用:

  • 開發多個不同的 GUI (例如面向普通使用者和管理員進行分別配置)
  • 支援多個不同的 API (例如, 能夠在 Windows、 Linux 和 macOS 上執行該程式)。

橋接模式結構圖
在這裡插入圖片描述

  1. 抽象部分 (Abstraction) 提供高層控制邏輯, 依賴於完成底層實際工作的實現物件。

  2. 實現部分 (Implementation) 為所有具體實現宣告通用介面。 抽象部分僅能通過在這裡宣告的方法與實現物件互動。

  • 抽象部分可以列出和實現部分一樣的方法, 但是抽象部分通常宣告一些複雜行為, 這些行為依賴於多種由實現部分宣告的原語操作。
  1. 具體實現 (Concrete Implementations) 中包括特定於平臺的程式碼。

  2. 精確抽象 (Refined Abstraction) 提供控制邏輯的變體。 與其父類一樣, 它們通過通用實現介面與不同的實現進行互動。

  3. 通常情況下, 客戶端 (Client) 僅關心如何與抽象部分合作。 但是, 客戶端需要將抽象物件與一個實現物件連線起來。

示例結構圖說明

示例演示了橋接模式如何拆分程式中同時管理裝置及其遙控器的龐雜程式碼。 ​ 裝置Device類作為實現部分, 而 遙控器Remote類則作為抽象部分。
在這裡插入圖片描述

最初類層次結構被拆分為兩個部分: 裝置和遙控器。

遙控器基類宣告瞭一個指向裝置物件的引用成員變數。 所有遙控器通過通用裝置介面與裝置進行互動, 使得同一個遙控器可以支援不同型別的裝置。

你可以開發獨立於裝置類的遙控器類, 只需新建一個遙控器子類即可。 例如, 基礎遙控器可能只有兩個按鈕, 但你可在其基礎上擴充套件新功能, 比如額外的一節電池或一塊觸控式螢幕。

客戶端程式碼通過遙控器建構函式將特定種類的遙控器與裝置物件連線起來。

橋接模式適用場景:

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

實現方式

  1. 明確類中獨立的維度。 獨立的概念可能是: 抽象/平臺, 域/基礎設施, 前端/後端或介面/實現。

  2. 瞭解客戶端的業務需求, 並在抽象基類中定義它們。

  3. 確定在所有平臺上都可執行的業務。 並在通用實現介面中宣告抽象部分所需的業務。

  4. 為你域內的所有平臺建立實現類, 但需確保它們遵循實現部分的介面。

  5. 在抽象類中新增指向實現型別的引用成員變數。 抽象部分會將大部分工作委派給該成員變數所指向的實現物件。

  6. 如果你的高層邏輯有多個變體, 則可通過擴充套件抽象基類為每個變體建立一個精確抽象。

  7. 客戶端程式碼必須將實現物件傳遞給抽象部分的建構函式才能使其能夠相互關聯。 此後, 客戶端只需與抽象物件進行互動, 無需和實現物件打交道。

C++程式碼示例:

#include <iostream>

/**
 * The Implementation defines the interface for all implementation classes. It
 * doesn't have to match the Abstraction's interface. In fact, the two
 * interfaces can be entirely different. Typically the Implementation interface
 * provides only primitive operations, while the Abstraction defines higher-
 * level operations based on those primitives.
 */

class Implementation {
 public:
  virtual ~Implementation() {}
  virtual std::string OperationImplementation() const = 0;
};

/**
 * Each Concrete Implementation corresponds to a specific platform and
 * implements the Implementation interface using that platform's API.
 */
class ConcreteImplementationA : public Implementation {
 public:
  std::string OperationImplementation() const override {
    return "ConcreteImplementationA: Here's the result on the platform A.\n";
  }
};
class ConcreteImplementationB : public Implementation {
 public:
  std::string OperationImplementation() const override {
    return "ConcreteImplementationB: Here's the result on the platform B.\n";
  }
};

/**
 * The Abstraction defines the interface for the "control" part of the two class
 * hierarchies. It maintains a reference to an object of the Implementation
 * hierarchy and delegates all of the real work to this object.
 */

class Abstraction {
  /**
   * @var Implementation
   */
 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();
  }
};
/**
 * You can extend the Abstraction without changing the Implementation classes.
 */
class ExtendedAbstraction : public Abstraction {
 public:
  ExtendedAbstraction(Implementation* implementation) : Abstraction(implementation) {
  }
  std::string Operation() const override {
    return "ExtendedAbstraction: Extended operation with:\n" +
           this->implementation_->OperationImplementation();
  }
};

/**
 * Except for the initialization phase, where an Abstraction object gets linked
 * with a specific Implementation object, the client code should only depend on
 * the Abstraction class. This way the client code can support any abstraction-
 * implementation combination.
 */
void ClientCode(const Abstraction& abstraction) {
  // ...
  std::cout << abstraction.Operation();
  // ...
}
/**
 * The client code should be able to work with any pre-configured abstraction-
 * implementation combination.
 */

int main() {
  Implementation* implementation = new ConcreteImplementationA;
  Abstraction* abstraction = new Abstraction(implementation);
  ClientCode(*abstraction);
  std::cout << std::endl;
  delete implementation;
  delete abstraction;

  implementation = new ConcreteImplementationB;
  abstraction = new ExtendedAbstraction(implementation);
  ClientCode(*abstraction);

  delete implementation;
  delete abstraction;

  return 0;
}

output:

Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B.

相關文章