Null Object パターン

null チェックの代わりに、何もしないオブジェクトを使って条件分岐を排除するデザインパターン

設計パターン型安全

Null Object パターンとは

Null Object パターンは、null を返す代わりに「何もしない」振る舞いを持つオブジェクトを返すことで、呼び出し側の null チェックを不要にするデザインパターンである。Bobby Woolf が 1996 年に Pattern Languages of Program Design で発表した。

Tony Hoare は null 参照の発明を「10 億ドルの過ち」と呼んだ。Null Object パターンは、null が引き起こす問題の一部を設計レベルで解決する。

Before / After

// ❌ null チェックがコード全体に散在
function processOrder(order: Order) {
  const logger = getLogger();
  if (logger !== null) logger.info('Processing started');

  const metrics = getMetrics();
  if (metrics !== null) metrics.increment('orders.processed');

  const notifier = getNotifier();
  if (notifier !== null) notifier.send('Order processed');
}

// ✅ Null Object パターン: null チェック不要
function processOrder(order: Order) {
  const logger = getLogger();     // NullLogger or ConsoleLogger
  const metrics = getMetrics();   // NullMetrics or DatadogMetrics
  const notifier = getNotifier(); // NullNotifier or SlackNotifier

  logger.info('Processing started');
  metrics.increment('orders.processed');
  notifier.send('Order processed');
}

実装

interface Logger {
  info(message: string): void;
  error(message: string, error?: Error): void;
}

class ConsoleLogger implements Logger {
  info(message: string) { console.log(`[INFO] ${message}`); }
  error(message: string, err?: Error) { console.error(`[ERROR] ${message}`, err); }
}

class NullLogger implements Logger {
  info(_message: string) { /* 何もしない */ }
  error(_message: string, _err?: Error) { /* 何もしない */ }
}

// ファクトリ関数で切り替え
function createLogger(enabled: boolean): Logger {
  return enabled ? new ConsoleLogger() : new NullLogger();
}

NullLogger はインターフェースを完全に実装するが、すべてのメソッドが何もしない。呼び出し側は Logger インターフェースだけを知っていればよく、実体が ConsoleLogger か NullLogger かを意識しない。

実務での活用パターン

テスト時のモック代替

テストで外部サービスへの通知を抑制する。

// テスト: 実際のメール送信を抑制
const service = new OrderService(
  new InMemoryOrderRepo(),
  new NullEmailSender(),   // メールを送らない
  new NullLogger(),        // ログを出さない
);

Optional Chaining との違い

TypeScript の ?. (Optional Chaining) は null チェックを簡潔に書く構文だが、Null Object パターンとは目的が異なる。

手法 目的 適するケース
Optional Chaining (?.) null アクセスの安全化 プロパティの参照
Null Object パターン 振る舞いのデフォルト化 メソッド呼び出しの分岐排除

user?.address?.city は値の参照に適しているが、logger?.info('...') のように振る舞いを持つオブジェクトには Null Object パターンの方が適切だ。

注意点

デバッグが難しくなる

NullLogger を使うと、エラーが発生してもログが出ない。本番環境で NullLogger が使われていないか確認する仕組みが必要だ。

過度な適用

すべての null を Null Object に置き換える必要はない。「値が存在しない」ことに意味がある場合 (検索結果が 0 件など) は、null や空配列を返す方が適切だ。

Null Object vs Optional vs null チェック

方式 安全性 可読性
null チェック (if) △ 忘れる可能性 △ ネストが深い
Optional/Maybe ✅ 型で強制
Null Object ✅ 何もしないオブジェクト

実践的な知識は関連書籍でも得られる。

関連用語