デコレーターパターン
デコレーターパターン(Decorator Pattern) は、既存のオブジェクトに対して動的に新しい機能を追加するためのデザインパターンです。このパターンは、オブジェクトをラップすることによって、新しい機能を柔軟に追加したり、既存のクラスの挙動を変更したりすることを可能にします。
デコレーターパターンは、継承の代替として使用され、特に複数の異なる機能を組み合わせて動的にオブジェクトに付加したい場合に有効です。TypeScriptでは、デコレーター構文を使用してこのパターンを実装できます。
TypeScriptでのデコレーターパターンの実装
TypeScriptには、クラス、メソッド、プロパティなどに対してデコレーターを適用できる機能があります。以下に、クラスに対して新しい機能を追加するデコレーターパターンの例を示します。
コーヒーに追加トッピングを行うデコレーターパターンの例
typescript
// コーヒーインターフェース
interface Coffee {
cost(): number;
description(): string;
}
// シンプルなコーヒーの実装
class SimpleCoffee implements Coffee {
cost(): number {
return 5;
}
description(): string {
return 'Simple Coffee';
}
}
// 抽象デコレータークラス
class CoffeeDecorator implements Coffee {
protected decoratedCoffee: Coffee;
constructor(coffee: Coffee) {
this.decoratedCoffee = coffee;
}
cost(): number {
return this.decoratedCoffee.cost();
}
description(): string {
return this.decoratedCoffee.description();
}
}
// ミルクデコレーター
class MilkDecorator extends CoffeeDecorator {
cost(): number {
return super.cost() + 2;
}
description(): string {
return super.description() + ', Milk';
}
}
// シナモンデコレーター
class CinnamonDecorator extends CoffeeDecorator {
cost(): number {
return super.cost() + 1.5;
}
description(): string {
return super.description() + ', Cinnamon';
}
}
// 実際の使用例
let myCoffee: Coffee = new SimpleCoffee();
console.log(`${myCoffee.description()} costs $${myCoffee.cost()}`); // Simple Coffee costs $5
myCoffee = new MilkDecorator(myCoffee);
console.log(`${myCoffee.description()} costs $${myCoffee.cost()}`); // Simple Coffee, Milk costs $7
myCoffee = new CinnamonDecorator(myCoffee);
console.log(`${myCoffee.description()} costs $${myCoffee.cost()}`); // Simple Coffee, Milk, Cinnamon costs $8.5
解説
Coffeeインターフェース
Coffee
インターフェースは、すべてのコーヒーが実装すべきcost
とdescription
メソッドを定義しています。
SimpleCoffeeクラス
SimpleCoffee
クラスは、基本的なコーヒーのコストと説明を提供します。このクラスは最もシンプルな形でのコーヒーを表します。
CoffeeDecoratorクラス(抽象デコレーター)
CoffeeDecorator
は、デコレーターパターンの基盤となるクラスです。このクラスはCoffee
インターフェースを実装しており、既存のCoffee
オブジェクトをラップして動作を拡張します。
具体的なデコレータークラス(MilkDecoratorとCinnamonDecorator)
MilkDecorator
とCinnamonDecorator
は、CoffeeDecorator
を継承し、それぞれミルクやシナモンを追加する役割を持っています。cost
メソッドで追加のコストを計算し、description
メソッドで説明を更新します。
実際の使用例
- 最初にシンプルなコーヒーを作成し、次にそれにミルクやシナモンを追加していきます。各デコレーターを追加するごとに、コーヒーのコストと説明が更新されます。
利点
- 柔軟性の向上: 継承を使わずに既存のオブジェクトに新しい機能を追加できるため、コードの柔軟性が向上します。
- オープン/クローズド原則に準拠: クラスの変更をせずに機能を拡張できるため、オープン/クローズド原則(拡張にはオープンだが、修正にはクローズド)を遵守できます。
使用例
- UIコンポーネントの装飾: ボタンにアイコンやラベルを動的に追加する場合など、UIコンポーネントのカスタマイズに使われます。
- データストリームの処理: JavaScriptやTypeScriptにおけるデータストリームの処理で、各ステップに機能を追加するために使用できます(例: ロギング、暗号化、圧縮など)。
まとめ
デコレーターパターンは、既存のオブジェクトに対して動的に機能を追加する柔軟な方法を提供します。TypeScriptのクラスとインターフェースを使ってこのパターンを実装することで、コードの再利用性と柔軟性を高めることが可能です。
このパターンを理解し、適切に利用することで、特に動的に機能を追加したり、複数の機能を組み合わせる必要がある場面で強力なツールとなります。