Decoratorパターン
GPT-4.icon
Decoratorパターンは、オブジェクト指向のデザインパターンの一つで、既存のクラスのコードを変更することなく、動的に新しい機能をオブジェクトに追加する方法です。このパターンは、継承の代わりに使用することで、システムの柔軟性を高めることができます。
具体的には、Decoratorパターンでは「デコレーター」と呼ばれるクラスが基本となるクラスのインタフェースを実装し、そのインスタンスを内部に保持します。デコレーターは基本的な機能に加えて新しい機能を提供するか、既存の機能を上書きします。
解決する問題
機能の硬直性: 既存のクラスに新しい機能を追加する場合、通常はクラスを修正または継承する必要がありますが、これによってクラスが硬直化し、変更が困難になることがあります。また、継承を用いると階層が深くなり、システムが複雑化する問題もあります。
機能の重複: 似たような機能が異なるクラスで必要になった場合、継承だけでは効率的にコードを再利用できないことが多く、コードの重複が発生しやすいです。
動的な機能追加の難しさ: 実行時にオブジェクトの振る舞いを変更する必要がある場合、継承を使用した方法では対応が難しいです。
解決策の構造
Decoratorパターンは、次のような構造で上記の問題を解決します。
Component:
コアとなる機能を提供するインターフェースまたは抽象クラス
すべてのコンポーネントとデコレーターがこのインターフェースを実装することで、一貫したインターフェースを保ちます。
ConcreteComponent:
Componentインターフェースを実装し、基本的な機能を提供するクラス。
Decorator:
Componentインターフェースを実装し、内部にComponentオブジェクトを持つ(これを「コンポーネントの装飾」に利用します)。
このクラスは、Componentのメソッドを呼び出す前後で追加の機能を実行することで、コンポーネントの振る舞いを拡張または変更します。
ConcreteDecorator:
Decoratorクラスを拡張し、具体的な拡張機能を提供します。
例えば、ログを出力する、データを加工する、条件に基づいて振る舞いを変えるなどの機能がこれにあたります。
TypeScriptでのDecoratorパターンのサンプルコードを以下に示します。
code:ts
// コンポーネントインターフェース
interface Component {
operation(): void;
}
// コンクリートコンポーネントクラス
class ConcreteComponent implements Component {
operation() {
console.log('ConcreteComponent operation.');
}
}
code:ts
// デコレーターベースクラス
abstract class Decorator implements Component {
protected component: Component;
constructor(component: Component) {
this.component = component;
}
operation() {
this.component.operation();
}
}
code:ts
// ログを出力する具体的なデコレーター
class LoggingDecorator extends Decorator {
operation() {
console.log('LoggingDecorator starts operation.');
this.component.operation();
console.log('LoggingDecorator ends operation.');
}
}
// エラーハンドリングを追加するデコレーター
class ErrorHandlingDecorator extends Decorator {
operation() {
try {
super.operation();
} catch (error) {
console.error('Error occurred:', error);
}
}
}
// パフォーマンスの計測を追加するデコレーター
class PerformanceDecorator extends Decorator {
operation() {
const startTime = performance.now();
super.operation();
const endTime = performance.now();
console.log(Operation took ${endTime - startTime} milliseconds.);
}
}
code:ts
// 使用例
function clientCode(component: Component) {
// コンポーネントの操作を実行
component.operation();
}
const simple = new ConcreteComponent();
console.log('Without decorator:');
clientCode(simple);
const decorated = new LoggingDecorator(simple);
console.log('With decorator:');
clientCode(decorated);