デコレーター
公開日: 2025/06/02
デコレーターパターンとは?──既存機能を壊さず拡張するスマートな設計
はじめに
ソフトウェア開発では、「既存のクラスを変更せずに、機能を追加したい」というニーズがよくあります。
そのような場面で強力に機能するのが「デコレーターパターン(Decorator Pattern)」です。
ラップするように機能を追加するこのパターンは、継承より柔軟で、拡張性の高い構造を実現します。この記事では、デコレーターパターンの仕組み、構造、用途、そして実装例を紹介します。
基本情報・概要
デコレーターパターンとは、あるオブジェクトに対して、動的に追加の機能を重ねていく設計パターンです。
既存のクラスを継承することなく、ラップ(包み込む)することで振る舞いを拡張します。
主な特徴:
- クラスの開放・閉鎖原則(OCP)に沿った機能追加
- 継承ではなく合成による柔軟な拡張
- 実行時に追加・組み合わせ可能な構造
比較・分類・特徴の表形式まとめ(任意)
要素 | 役割・説明 |
---|---|
コンポーネント | 基本インターフェースまたは抽象クラス |
具体コンポーネント | 元になるオブジェクト(例:基本クラス) |
デコレーター | コンポーネントを内包し、追加の振る舞いを付与するクラス |
ネスト可能性 | 複数のデコレーターを連続でラップ可能(順序によって結果が変化) |
デコレータは“機能の重ね着”ともいえる構造です。
深掘り解説
✅ JavaScriptでの実装例
class Coffee { cost() { return 300; } } class MilkDecorator { constructor(base) { this.base = base; } cost() { return this.base.cost() + 50; } } class SugarDecorator { constructor(base) { this.base = base; } cost() { return this.base.cost() + 30; } } let drink = new Coffee(); drink = new MilkDecorator(drink); drink = new SugarDecorator(drink); console.log(drink.cost()); // 380
とMilkDecorator
はSugarDecorator
を包んで拡張Coffee
メソッドは「積み重なった結果」を返すcost()
- オブジェクト単位での動的な機能拡張が可能
応用・発展的な使い方
- ロギングの追加:既存の処理にログ出力機能を加える
- アクセス制御:条件に応じて処理をスキップ・変更
- キャッシュデコレーション:関数の結果をキャッシュ
- ミドルウェア構造(Expressなど):処理を重ねて通過させる構造に応用
デコレータは“既存機能の外に追加する”という柔軟な設計が魅力です。
よくある誤解と注意点(任意)
- 継承との違いが曖昧になる:クラス拡張ではなく「機能ラップ」が本質
- ネストが深くなりすぎると混乱:装飾の順序と意味が分かりにくくなる
- インターフェースの一貫性が必須:ラップ対象と同じメソッドを実装する必要あり
- 無意味なデコレータの乱用:すべてをデコレートするのは過剰設計になりがち
デコレータは強力だが構造が見えにくくなるリスクも持つため、設計意図の明示が重要です。
まとめ
デコレーターパターンは、既存のコードに手を加えることなく、機能を柔軟に拡張できる設計手法です。
継承の限界を超えて、実行時にも構成を変えられるこのパターンは、拡張性や再利用性を重視するプロジェクトに非常に有効です。
小さなラッパーから始めて、「構造を壊さずに拡張する」思考を身につけましょう。