Decorator パターン実装

既存オブジェクトの振る舞いを動的に拡張し、継承を使わずに機能を追加するデザインパターン

設計パターンTypeScript

Decorator パターンとは

Decorator パターンは、既存のオブジェクトをラップして振る舞いを動的に追加する GoF デザインパターンである。継承ではなくコンポジションで機能を拡張するため、機能の組み合わせが自由で、組み合わせの爆発を避けられる。

基本的な実装

interface Logger {
  log(message: string): void;
}

class ConsoleLogger implements Logger {
  log(message: string) { console.log(message); }
}

// Decorator: タイムスタンプを追加
class TimestampLogger implements Logger {
  constructor(private inner: Logger) {}
  log(message: string) {
    this.inner.log(`[${new Date().toISOString()}] ${message}`);
  }
}

// Decorator: ログレベルを追加
class LevelLogger implements Logger {
  constructor(private inner: Logger, private level: string) {}
  log(message: string) {
    this.inner.log(`[${this.level}] ${message}`);
  }
}

// 組み合わせて使う (順序を変えると出力が変わる)
const logger = new TimestampLogger(new LevelLogger(new ConsoleLogger(), 'INFO'));
logger.log('Server started');
// → [2026-03-22T10:00:00.000Z] [INFO] Server started

Decorator は同じインターフェースを実装し、内部に別の実装を持つ。これにより、任意の順序で任意の数の Decorator を重ねられる。

継承との比較

// ❌ 継承: 組み合わせが爆発する
class TimestampConsoleLogger extends ConsoleLogger { /* ... */ }
class LevelConsoleLogger extends ConsoleLogger { /* ... */ }
class TimestampLevelConsoleLogger extends ConsoleLogger { /* ... */ }
class TimestampFileLogger extends FileLogger { /* ... */ }
// → 機能 N 個 × ロガー M 種 = N×M クラスが必要

// ✅ Decorator: 自由に組み合わせ
const logger = new TimestampLogger(new LevelLogger(new FileLogger()));
// → 機能 N 個 + ロガー M 種 = N+M クラスで済む

関数ベースの Decorator

TypeScript/JavaScript では、クラスを使わず高階関数で Decorator を実装する方が簡潔な場合が多い。

TypeScript 5.0 の Decorator 構文

TypeScript 5.0 で TC39 Stage 3 の Decorator 構文が導入された。

function log(target: any, context: ClassMethodDecoratorContext) {
  return function (this: any, ...args: any[]) {
    console.log(`${String(context.name)} called with`, args);
    const result = target.apply(this, args);
    console.log(`${String(context.name)} returned`, result);
    return result;
  };
}

class OrderService {
  @log
  calculateTotal(items: Item[]): number {
    return items.reduce((sum, item) => sum + item.price, 0);
  }
}

実務での応用

場面 Decorator の例
Express ミドルウェア 認証、ログ、CORS、レート制限
AWS SDK ミドルウェア リトライ、ログ、メトリクス
Lambda ハンドラー エラーハンドリング、認証、バリデーション
React HOC 認証ガード、ローディング表示

全体像を把握するには関連書籍も有用。

関連用語