開放閉鎖の原則

SOLID の O - ソフトウェアの構成要素は拡張に対して開かれ、修正に対して閉じているべきという設計原則

SOLID設計原則

開放閉鎖の原則とは

開放閉鎖の原則 (Open-Closed Principle, OCP) は、SOLID 原則の O にあたり、「ソフトウェアの構成要素は拡張に対して開かれ (Open)、修正に対して閉じている (Closed) べき」という設計原則である。

Bertrand Meyer が 1988 年の著書「Object-Oriented Software Construction」で定義した。Martin が引用する Meyer の原文では、拡張に使える状態を「開いている」、安定した仕様を公開して他のモジュールから利用できる状態を「閉じている」と呼び、この 2 つを同時に満たすモジュール分割を求めている。Robert C. Martin は 2000 年の解説「Design Principles and Design Patterns」でこれを「モジュールを修正せずに振る舞いを拡張できる」という形に言い換え、抽象への依存と動的ポリモーフィズムを実現手段の中心に据えた。

なぜ重要か

既存コードを修正すると、そのコードに依存する全箇所に回帰リスクが生じる。決済処理に新しい決済手段を追加するたびに if-else を追加していると、既存の決済手段のロジックが壊れるリスクがある。OCP に従えば、新しい決済手段を「追加」するだけで、既存コードには触れない。

違反の典型例

注文の割引率を顧客タイプで振り分ける処理を例にとる。

// ❌ OCP 違反: 新しい割引タイプを追加するたびに既存コードを修正
function calculateDiscount(order: Order): number {
  if (order.type === 'regular') {
    return order.total * 0.05;
  } else if (order.type === 'premium') {
    return order.total * 0.10;
  } else if (order.type === 'vip') {  // ← 追加のたびに修正
    return order.total * 0.15;
  }
  return 0;
}

新しい顧客タイプが増えるたびに calculateDiscount を修正する必要がある。修正のたびに既存の割引ロジックが壊れるリスクがある。

OCP に従った設計

同じ処理を、割引の計算そのものを差し替えられる部品として切り出す形に組み替える。

// ✅ OCP 準拠: インターフェースで拡張ポイントを定義
interface DiscountStrategy {
  calculate(order: Order): number;
}

class RegularDiscount implements DiscountStrategy {
  calculate(order: Order): number { return order.total * 0.05; }
}

class PremiumDiscount implements DiscountStrategy {
  calculate(order: Order): number { return order.total * 0.10; }
}

// 新しい割引タイプの追加: 既存コードを修正せず、新しいクラスを追加するだけ
class VipDiscount implements DiscountStrategy {
  calculate(order: Order): number { return order.total * 0.15; }
}

function calculateDiscount(order: Order, strategy: DiscountStrategy): number {
  return strategy.calculate(order);
}

VipDiscount を追加しても、RegularDiscountPremiumDiscount のコードには一切触れない。これが「拡張に対して開かれ、修正に対して閉じている」状態だ。

OCP を実現するパターン

どの定石を選ぶかは、差し替えたい単位がアルゴリズム全体なのか、処理フローの一部なのか、後から足す通知や装飾なのかで決まる。

パターン仕組み適するケース
Strategyインターフェースで振る舞いを差し替えアルゴリズムの切り替え
Template Method基底クラスで骨格を定義、サブクラスで詳細を実装処理フローが共通の場合
Observerイベントリスナーの追加で機能拡張通知・イベント処理
Decorator既存オブジェクトをラップして機能追加横断的関心事の追加

過剰適用の罠

OCP を厳密に適用しすぎると、すべての分岐をインターフェースとクラスに分離することになり、コードが過度に抽象化される。2〜3 種類しかない分岐を Strategy パターンで実装するのはオーバーエンジニアリングだ。

判断基準は「この分岐は今後増える可能性が高いか」である。決済手段や通知チャネルのように拡張が予想される箇所には OCP を適用し、変更の可能性が低い箇所には素直な if-else で十分だ。

さらに掘り下げるなら関連書籍が参考になる。

この記事は役に立ちましたか?

関連用語

関連する記事