Python ブリッジパターン

ブリッジパターンは構造型デザインパターンで、抽象部分と実装部分を分離し、それぞれを独立して変更できるようにします。簡単に言うと、つまり物事の複数の変化軸を分割し、各軸を独立して拡張できるようにすることです。。

身近なブリッジパターンの例え

リモコンとテレビの関係を想像してみてください:

  • リモコン(抽象部分):制御インターフェースを提供する(電源、音量、チャンネル)
  • テレビ(実装部分):具体的に操作を実行する(ソニーテレビ、サムスンテレビ、Xiaomiテレビ)

リモコンは具体的にどのテレビかを知る必要はなく、テレビもどのリモコンかを知る必要はありません。両者は「橋」を介して接続されており、それぞれ独立してアップグレード・モデルチェンジできます。


なぜブリッジパターンが必要なのか?

従来方式の限界

異なる形状と色をサポートする図形描画システムを設計するとしましょう:

例

# 従来の実装 - クラス爆発の問題
class RedCircle:
    def draw(self):
        print("赤い円を描画")

class BlueCircle:
    def draw(self):
        print("青い円を描画")

class RedRectangle:
    def draw(self):
        print("赤い長方形を描画")

class BlueRectangle:
    def draw(self):
        print("青い長方形を描画")

# 色や形状を追加するたびに、新しいクラスを作成する必要がある
# 3色 × 3形状 = 9クラス!

ブリッジパターンの利点

例

# ブリッジパターンの実装
class Color:
    """色インターフェース - 実装部分"""
    def apply_color(self):
        pass

class Shape:
    """形状抽象 - 抽象部分"""
    def __init__(self, color):
        self.color = color  # ブリッジの鍵!
   
    def draw(self):
        pass

# 必要なのは:3色 + 3形状 = 6クラス
# ではなく:3 × 3 = 9クラス

ブリッジパターンの核となる構造

完全な例を通してブリッジパターンの構成要素を理解しましょう:

4つの重要な役割

  1. 抽象部分(Abstraction):抽象インターフェースを定義し、実装部分への参照を保持する
  2. 具体抽象(RefinedAbstraction):抽象インターフェースを拡張する
  3. 実装部分(Implementor):実装インターフェースを定義する
  4. 具体実装(ConcreteImplementor):具体的にインターフェースを実装する

完全なコード例:図形描画システム

ステップ1:実装部分のインターフェースを定義する(色)

例

from abc import ABC, abstractmethod

class Color(ABC):
    """色インターフェース - 実装部分"""
    @abstractmethod
    def apply_color(self):
        pass

class Red(Color):
    """具体実装 - 赤"""
    def apply_color(self):
        return "赤"

class Blue(Color):
    """具体実装 - 青"""
    def apply_color(self):
        return "青"

class Green(Color):
    """具体実装 - 緑"""
    def apply_color(self):
        return "緑"

ステップ2:抽象部分を定義する(形状)

例

class Shape(ABC):
    """形状抽象 - 抽象部分"""
    def __init__(self, color: Color):
        self.color = color  # ブリッジの鍵:実装部分の参照を保持する
   
    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    """具体抽象 - 円"""
    def draw(self):
        color_name = self.color.apply_color()
        print(f"{color_name}の円を描画")

class Rectangle(Shape):
    """具体抽象 - 長方形"""
    def draw(self):
        color_name = self.color.apply_color()
        print(f"{color_name}の長方形を描画")

class Triangle(Shape):
    """具体抽象 - 三角形"""
    def draw(self):
        color_name = self.color.apply_color()
        print(f"{color_name}の三角形を描画")

ステップ3:ブリッジパターンを使用する

例

# クライアントコード
def main():
    # 色の実装を作成
    red = Red()
    blue = Blue()
    green = Green()
   
    # 形状抽象を作成し、色の実装をブリッジする
    red_circle = Circle(red)
    blue_rectangle = Rectangle(blue)
    green_triangle = Triangle(green)
   
    # 図形を描画
    print("=== 図形描画結果 ===")
    red_circle.draw()        # 出力:赤い円を描画
    blue_rectangle.draw()    # 出力:青い長方形を描画
    green_triangle.draw()    # 出力:緑の三角形を描画
   
    # 柔軟な組み合わせ:赤い長方形
    red_rectangle = Rectangle(red)
    red_rectangle.draw()     # 出力:赤い長方形を描画

if __name__ == "__main__":
    main()

実際の適用シーン

シーン1:メッセージ送信システム

例

from abc import ABC, abstractmethod

# 実装部分:メッセージ送信方法
class MessageSender(ABC):
    @abstractmethod
    def send(self, message):
        pass

class EmailSender(MessageSender):
    def send(self, message):
        return f"メールで送信:{message}"

class SMSSender(MessageSender):
    def send(self, message):
        return f"SMSで送信:{message}"

class WeChatSender(MessageSender):
    def send(self, message):
        return f"WeChatで送信:{message}"

# 抽象部分:メッセージタイプ
class Message(ABC):
    def __init__(self, sender: MessageSender):
        self.sender = sender
   
    @abstractmethod
    def send(self):
        pass

class UrgentMessage(Message):
    def send(self):
        message = "[緊急] " + self.get_content()
        return self.sender.send(message)
   
    def get_content(self):
        return "システムに異常が発生しました。至急対応してください!"

class NormalMessage(Message):
    def __init__(self, sender: MessageSender, content):
        super().__init__(sender)
        self.content = content
   
    def send(self):
        return self.sender.send(self.content)

# 使用例
def message_example():
    email_sender = EmailSender()
    sms_sender = SMSSender()
   
    # 緊急メッセージをSMSで送信
    urgent_sms = UrgentMessage(sms_sender)
    print(urgent_sms.send())  # SMSで送信:[緊急] システムに異常が発生しました。至急対応してください!
   
    # 通常メッセージをメールで送信
    normal_email = NormalMessage(email_sender, "月次レポートが生成されました")
    print(normal_email.send())  # メールで送信:月次レポートが生成されました

message_example()

シーン2:デバイスリモコンシステム

例

# 実装部分:デバイス
class Device(ABC):
    @abstractmethod
    def turn_on(self):
        pass
   
    @abstractmethod
    def turn_off(self):
        pass
   
    @abstractmethod
    def set_volume(self, volume):
        pass

class TV(Device):
    def turn_on(self):
        return "テレビがオンになりました"
   
    def turn_off(self):
        return "テレビがオフになりました"
   
    def set_volume(self, volume):
        return f"テレビの音量が設定されました:{volume}"

class Radio(Device):
    def turn_on(self):
        return "ラジオがオンになりました"
   
    def turn_off(self):
        return "ラジオがオフになりました"
   
    def set_volume(self, volume):
        return f"ラジオの音量が設定されました:{volume}"

# 抽象部分:リモコン
class RemoteControl:
    def __init__(self, device: Device):
        self.device = device
   
    def toggle_power(self):
        return "電源状態を切り替える"
   
    def volume_up(self):
        return "音量を上げる"
   
    def volume_down(self):
        return "音量を下げる"

class AdvancedRemoteControl(RemoteControl):
    def mute(self):
        return "ミュートモード"
   
    def set_channel(self, channel):
        return f"チャンネルを切り替える:{channel}"

# 使用例
def remote_example():
    tv = TV()
    radio = Radio()
   
    basic_remote = RemoteControl(tv)
    advanced_remote = AdvancedRemoteControl(radio)
   
    print(basic_remote.toggle_power())  # 電源状態を切り替える
    print(advanced_remote.mute())       # ミュートモード

remote_example()

ブリッジパターンの利点の比較

特性 従来方式 ブリッジパターン
拡張性 悪い、既存コードの修正が必要 良い、独立して拡張可能
クラス数 多い、クラス爆発が起こりやすい 少ない、線形に増加
保守性 困難、一箇所変更すると全体に影響 容易、責任の分離
柔軟性 固定の組み合わせ 実行時の動的組み合わせ

ベストプラクティスと注意点

使用するタイミング

✅ ブリッジパターンの使用に適した状況:

  • 複数の軸で独立して拡張する必要がある場合
  • 実行時に実装を切り替える必要がある場合
  • 抽象と実装を恒久的にバインドすることを避ける場合
  • 継承によってクラス階層が複雑になりすぎる場合

❌ 適さない状況:

  • 変化の軸が少なく、複雑な設計が不要な場合
  • 抽象と実装の間に強い結合関係がある場合

よくある間違いと回避方法

例

# 誤った例:本当に分離できていない
class BadShape:
    def __init__(self, color_type):
        if color_type == "red":
            self.color = Red()
        elif color_type == "blue":
            self.color = Blue()
        # 新しい色を追加するには、まだこのクラスを修正する必要がある

# 正しい方法:依存性注入
class GoodShape:
    def __init__(self, color: Color):  # 具体的な実装ではなく、インターフェースに依存する
        self.color = color

練習問題

練習1:図形システムの拡張

図形システムに以下の機能を追加してください:

  1. 新しい色(黄色)を追加
  2. 新しい形状(楕円形)を追加
  3. 黄色の楕円形を作成して描画する

練習2:決済システムの設計

決済システムを設計してください。要件:

  • 支払い方法:Alipay、WeChat Pay、銀行カード
  • 支払いタイプ:通常支払い、分割支払い、組み合わせ支払い
  • ブリッジパターンを使用して、支払い方法と支払いタイプを独立して拡張できるようにします。

まとめ

ブリッジパターンは抽象と実装を分離することで、非常に高い柔軟性を提供します。その中核となる考え方は次のとおりです。

  1. 変化の次元を特定する:システム内で独立して変化し得る複数の次元を見つけ出す
  2. 橋を架ける:組み合わせ関係を通じて異なる次元を接続する
  3. 独立して拡張する:各次元は独立して発展・変化できる
その他の拡張