2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SOLID原則② OCP/LSP/DIP:抽象の設計原則とその限界

Posted at

概要

オブジェクト指向における設計原則の核とも言えるのが、OCP / LSP / DIP の三原則。
いずれも「抽象」による拡張性と柔軟性を担保するために存在するが、
それらは正しく適用しなければ、かえって設計を肥大化させ、破綻させる危険性を孕んでいる。

本稿ではこの3原則を、それぞれの設計意図と実装パターン・限界点の両面から、構造的に再定義する。


1. OCP:Open/Closed Principle(開放・閉鎖の原則)

「ソフトウェアの構成要素は拡張に対して開かれていなければならず、修正に対して閉じていなければならない」

✅ 本質:

  • 仕様変更・新機能追加を既存コードの改変なく実現する
  • → そのためには抽象を前提とした設計が必要

✅ パターン例(Strategy)

interface TaxCalculator {
  calculate(price: number): number
}

class JapanTax implements TaxCalculator {
  calculate(price: number) {
    return price * 1.1
  }
}

class USATax implements TaxCalculator {
  calculate(price: number) {
    return price * 1.07
  }
}

TaxCalculator を基準にすれば、新しい国の税率もクラス追加のみで拡張可能


2. LSP:Liskov Substitution Principle(リスコフの置換原則)

「派生型は、基底型として置き換え可能でなければならない」

✅ 本質:

  • 継承は「型の置換性」を保証して初めて成立する
  • → 派生クラスが親の契約を破るなら、それは設計違反

❌ 違反例:

class Rectangle {
  constructor(public width: number, public height: number) {}
}

class Square extends Rectangle {
  constructor(size: number) {
    super(size, size)
  }

  set width(w: number) {
    this.width = w
    this.height = w
  }
}

Rectangle として扱うと 「w ≠ h」前提が崩壊し、予測不能な動作に

✅ 対策:

  • 継承の代わりに 委譲 または 共通インターフェース による設計に切り替える

3. DIP:Dependency Inversion Principle(依存性逆転の原則)

「高水準のモジュールは、低水準のモジュールに依存すべきではない。両者は抽象に依存すべきである」

✅ 本質:

  • 実装(詳細)ではなく、抽象(契約)に依存する
  • 実行側・呼び出される側を疎結合に保つための構造的基準

✅ 実装例(DIによる逆転)

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

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

class UserService {
  constructor(private logger: Logger) {}

  createUser(name: string) {
    this.logger.log(`Created user: ${name}`)
  }
}

UserServiceLogger の抽象だけに依存
→ 実装の切り替えは差し替えだけで可能


4. 原則の設計判断フロー

OCP:
  - 新しいロジック追加時、既存コードの変更が必要か? → YES → 拡張ポイント不足
  - 抽象で構造化されているか? → NO → インターフェース導入を検討

LSP:
  - 派生クラスは常に親クラスの契約を満たしているか? → NO → 継承設計の見直し

DIP:
  - 上位モジュールが下位実装に依存していないか? → YES → 抽象化レイヤーで逆転可能か?

5. 限界と誤用への注意

❌ 「すべての依存を抽象化すべき」

→ ✅ 小規模構成にまでDIやOCPを導入すると、設計が複雑化しすぎてオーバーヘッドになる


❌ 「LSPを守るには継承をやめるべき」

→ ✅ 問題は継承そのものではなく、置換性を考慮しない“安易な継承”


❌ 「原則を守れば設計は良くなる」

→ ✅ 原則は設計判断の補助ツールであり、目的ではない


結語

OCP / LSP / DIP ―
これらはオブジェクト指向設計における「抽象」を中核とした構造原則である。

  • OCP は変更を「追加」で解決する構造を作り
  • LSP は継承の「安全性」を定義し
  • DIP は依存の「方向性」を逆転して柔軟性を確保する

だがそれらを盲信するのではなく、
目的=“変化に強い構造”のために、必要な時に選び取るべきである。

原則とは、
“設計の自由を構造的に維持するための、思考のための道具である。”

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?