Skip to content

ブリッジパターン

公開日:November 17, 2024更新日:November 28, 2024
TypeScriptDesign pattern📄

ブリッジパターン

ブリッジパターン(Bridge Pattern) は、抽象部分実装部分を分離することで、独立して変更・拡張できるようにするデザインパターンです。このパターンは、インターフェースやクラスの階層構造を柔軟に管理し、クラスの拡張が爆発的に増えることを防ぐのに役立ちます。

ブリッジパターンは、機能の階層と実装の階層を分けて実装することで、両者を独立して拡張できる利点があります。たとえば、デバイスに依存する機能と、そのデバイス自体の特性を分けて実装することで、より柔軟で再利用可能な設計を作ることができます。

TypeScriptでのブリッジパターンの実装

以下に、TypeScriptでブリッジパターンを実装する例を紹介します。この例では、デバイス(テレビやラジオなど)とリモコン(通常のリモコンや高度なリモコン)を独立して定義し、自由に組み合わせられるようにします。

デバイスとリモコンの例

typescript
// 実装のインターフェース(Implementor)
interface Device {
  isEnabled(): boolean;
  enable(): void;
  disable(): void;
  getVolume(): number;
  setVolume(percent: number): void;
}

// 具体的なデバイスクラス(Concrete Implementor)
class TV implements Device {
  private enabled: boolean = false;
  private volume: number = 50;

  isEnabled(): boolean {
    return this.enabled;
  }

  enable(): void {
    this.enabled = true;
    console.log('TV is now ON');
  }

  disable(): void {
    this.enabled = false;
    console.log('TV is now OFF');
  }

  getVolume(): number {
    return this.volume;
  }

  setVolume(percent: number): void {
    this.volume = percent;
    console.log(`TV volume set to ${percent}%`);
  }
}

class Radio implements Device {
  private enabled: boolean = false;
  private volume: number = 30;

  isEnabled(): boolean {
    return this.enabled;
  }

  enable(): void {
    this.enabled = true;
    console.log('Radio is now ON');
  }

  disable(): void {
    this.enabled = false;
    console.log('Radio is now OFF');
  }

  getVolume(): number {
    return this.volume;
  }

  setVolume(percent: number): void {
    this.volume = percent;
    console.log(`Radio volume set to ${percent}%`);
  }
}

// 抽象部分(Abstraction)
class RemoteControl {
  protected device: Device;

  constructor(device: Device) {
    this.device = device;
  }

  togglePower(): void {
    if (this.device.isEnabled()) {
      this.device.disable();
    } else {
      this.device.enable();
    }
  }

  setVolume(percent: number): void {
    this.device.setVolume(percent);
  }
}

// 拡張された抽象部分(Refined Abstraction)
class AdvancedRemoteControl extends RemoteControl {
  mute(): void {
    this.device.setVolume(0);
    console.log('Device is muted');
  }
}

// 実際の使用例
const tv = new TV();
const radio = new Radio();

// 通常のリモコンでTVを操作
const remote = new RemoteControl(tv);
remote.togglePower(); // TV is now ON
remote.setVolume(75); // TV volume set to 75%

// 高度なリモコンでラジオを操作
const advancedRemote = new AdvancedRemoteControl(radio);
advancedRemote.togglePower(); // Radio is now ON
advancedRemote.mute(); // Radio volume set to 0, Device is muted

解説

  1. Deviceインターフェース(Implementor)

    • Deviceインターフェースは、デバイスが持つべき共通の操作(電源のON/OFF、音量の設定など)を定義しています。
  2. 具体的なデバイスクラス(Concrete Implementor)

    • TVRadioは、それぞれDeviceインターフェースを実装しており、デバイスごとの具体的な操作を提供します。
  3. RemoteControlクラス(Abstraction)

    • RemoteControlクラスは、Deviceインターフェースを使ってデバイスを操作する抽象部分です。具体的なデバイスの実装には依存しないため、異なるデバイスを同じリモコンで操作できます。
  4. AdvancedRemoteControlクラス(Refined Abstraction)

    • AdvancedRemoteControlは、RemoteControlを拡張し、追加機能(ミュート機能など)を提供しています。
  5. 実際の使用例

    • RemoteControlAdvancedRemoteControlは、異なるデバイス(TVRadio)を操作します。このように、抽象部分と実装部分を独立して組み合わせることで、柔軟な設計が可能になります。

利点

  • 柔軟な拡張: 抽象部分と実装部分を独立して変更できるため、新しいリモコンやデバイスを追加する場合でも、他の部分に影響を与えません。
  • クラスの爆発的増加の回避: 抽象部分と実装部分を分けることで、継承によるクラスの組み合わせが増えすぎる問題を防ぎます。
  • 再利用性の向上: 抽象部分と実装部分を分けることで、コードの再利用性が向上します。

使用例

  • UIとプラットフォームの分離: UIの抽象部分と、プラットフォームごとの描画部分を分けることで、同じUIロジックを異なるプラットフォームに適用できます。
  • データベース接続: 抽象的なデータベース操作と、具体的なデータベース(MySQL、PostgreSQLなど)を分離することで、異なるデータベースへの対応が容易になります。

まとめ

ブリッジパターンは、抽象部分と実装部分を独立して変更可能にすることで、柔軟で拡張性の高い設計を実現するデザインパターンです。TypeScriptを使った実装により、リモコンとデバイスの例を通じて、抽象部分と実装部分の分離のメリットを理解できたかと思います。

このパターンを適切に活用することで、特に複雑なシステムや多くのバリエーションを持つ機能を管理する際に、コードの保守性と拡張性を向上させることができます。