ブリッジパターン
ブリッジパターン(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
解説
Deviceインターフェース(Implementor)
Device
インターフェースは、デバイスが持つべき共通の操作(電源のON/OFF、音量の設定など)を定義しています。
具体的なデバイスクラス(Concrete Implementor)
TV
とRadio
は、それぞれDevice
インターフェースを実装しており、デバイスごとの具体的な操作を提供します。
RemoteControlクラス(Abstraction)
RemoteControl
クラスは、Device
インターフェースを使ってデバイスを操作する抽象部分です。具体的なデバイスの実装には依存しないため、異なるデバイスを同じリモコンで操作できます。
AdvancedRemoteControlクラス(Refined Abstraction)
AdvancedRemoteControl
は、RemoteControl
を拡張し、追加機能(ミュート機能など)を提供しています。
実際の使用例
RemoteControl
とAdvancedRemoteControl
は、異なるデバイス(TV
やRadio
)を操作します。このように、抽象部分と実装部分を独立して組み合わせることで、柔軟な設計が可能になります。
利点
- 柔軟な拡張: 抽象部分と実装部分を独立して変更できるため、新しいリモコンやデバイスを追加する場合でも、他の部分に影響を与えません。
- クラスの爆発的増加の回避: 抽象部分と実装部分を分けることで、継承によるクラスの組み合わせが増えすぎる問題を防ぎます。
- 再利用性の向上: 抽象部分と実装部分を分けることで、コードの再利用性が向上します。
使用例
- UIとプラットフォームの分離: UIの抽象部分と、プラットフォームごとの描画部分を分けることで、同じUIロジックを異なるプラットフォームに適用できます。
- データベース接続: 抽象的なデータベース操作と、具体的なデータベース(MySQL、PostgreSQLなど)を分離することで、異なるデータベースへの対応が容易になります。
まとめ
ブリッジパターンは、抽象部分と実装部分を独立して変更可能にすることで、柔軟で拡張性の高い設計を実現するデザインパターンです。TypeScriptを使った実装により、リモコンとデバイスの例を通じて、抽象部分と実装部分の分離のメリットを理解できたかと思います。
このパターンを適切に活用することで、特に複雑なシステムや多くのバリエーションを持つ機能を管理する際に、コードの保守性と拡張性を向上させることができます。