Topiqlo ロゴ

デコレーター

公開日: 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など):処理を重ねて通過させる構造に応用

デコレータは“既存機能の外に追加する”という柔軟な設計が魅力です。

よくある誤解と注意点(任意)

  • 継承との違いが曖昧になる:クラス拡張ではなく「機能ラップ」が本質
  • ネストが深くなりすぎると混乱:装飾の順序と意味が分かりにくくなる
  • インターフェースの一貫性が必須:ラップ対象と同じメソッドを実装する必要あり
  • 無意味なデコレータの乱用:すべてをデコレートするのは過剰設計になりがち

デコレータは強力だが構造が見えにくくなるリスクも持つため、設計意図の明示が重要です。

まとめ

デコレーターパターンは、既存のコードに手を加えることなく、機能を柔軟に拡張できる設計手法です。
継承の限界を超えて、実行時にも構成を変えられるこのパターンは、拡張性や再利用性を重視するプロジェクトに非常に有効です。
小さなラッパーから始めて、「構造を壊さずに拡張する」思考を身につけましょう。