9
8

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とは?

Last updated at Posted at 2026-01-10

SOLIDとは

2000年代初頭にロバート・C・マーチンによって整理された、オブジェクト指向設計において、変更に強く、理解しやすく、再利用性の高いソフトウェアを作るための5つの原則です。

以下5つの原則が定義されています。

  1. S:単一責任の原則 (Single Responsibility Principle)
  2. O:開放閉鎖の原則 (Open-Closed Principle)
  3. L:リスコフの置換原則 (Liskov Substitution Principle)
  4. I:インターフェース分離の原則 (Interface Segregation Principle)
  5. D:依存性逆転の原則 (Dependency Inversion Principle)

S:単一責任原則(SRP)

クラスを変更する理由は、たったひとつであるべきという考え方です。 1つのクラスに「データの保存」「ログの出力」「計算処理」など、複数の役割を持たせすぎないようにします。

→ 特定の機能を修正した際に、無関係なはずの場所でバグが発生する「予期せぬ副作用」と、コードの複雑化を防ぎます。

一つのクラスに複数の機能:

class UserService {
  saveUser() {}
  sendEmail() {}
}

一つのクラスに単一の機能:

class UserRepository {
  save() {}
}

class EmailService {
  send() {}
}

O:開放閉鎖原則(OCP)

拡張に対しては開いており、修正に対しては閉じているべきという考え方です。

  • 拡張に対して開いている(Open): 新しい機能や振る舞いを自由に追加できること。
  • 修正に対して閉じている(Closed): 新しい機能を追加するときに、すでに動いている(テスト済みの)ソースコードを書き換えなくてよいこと。

→ 新機能を追加するたびに既存のテスト済みコードを書き換えることで発生する、バグの混入(デグレード)と修正作業の連鎖を防ぎます。

if を追加し続ける:

if (type === "A") {}
else if (type === "B") {}

抽象的なクラス(インターフェース)の利用:

// ポリモーフィズムを使う
interface Strategy {
  execute(): void
}
// 処理ごとにクラスを分ける
class StrategyA implements Strategy {
  execute() {
    console.log("Aの処理")
  }
}

class StrategyB implements Strategy {
  execute() {
    console.log("Bの処理")
  }
}
// 呼び出し側
function run(strategy: Strategy) {
  strategy.execute()
}
run(new StrategyA())
run(new StrategyB())

L:リスコフの置換原則(LSP)

プログラムの中の、ある型(親クラス)のオブジェクトを、その派生型(子クラス)で置き換えても、正しく動作しなければならないという考え方です。簡単にいうと、「親クラスができることは、子クラスも100%同じようにできなきゃダメ」ということです。

→ 壊れた継承(開発者が「共通点があるから」という理由だけで安易に継承を使うこと)による、プログラムが予期せぬ動きをするバグを防ぎます。

子クラスで親クラスの操作が動かない:

// 親クラス
class Animal {
  move() {
    console.log("動く")
  }
}
// 子クラス
class Snake extends Animal {
  move() {
    throw new Error("動けません")
  }
}
// 使う側
function run(animal: Animal) {
  animal.move()
}

run(new Snake()) // 実行時エラー

親クラスの操作が子クラスで動く:

abstract class Animal {
  abstract move(): void
}
class Dog extends Animal {
  move() {
    console.log("走る")
  }
}

class Bird extends Animal {
  move() {
    console.log("飛ぶ")
  }
}
run(new Dog())
run(new Bird())

I:インターフェース分離原則(ISP)

利用しないメソッドを、利用者に強制的に実装させてはいけないという考え方です。巨大で汎用的なインターフェースを1つ作るのではなく、クライアントが必要とする機能だけに絞った小さなインターフェースに分割します。

→「自分には関係ない機能」まで実装(あるいは依存)せざるを得なくなるという問題を解決します。

太いインターフェース:

interface Machine {
  print()
  scan()
  fax()
}

小さなインターフェース:

interface Printer { print() }
interface Scanner { scan() }

D:依存性逆転原則(DIP)

上位モジュールは下位モジュールに依存してはならない。両者は抽象に依存すべきであるという原則です。 具体的なクラス(実装)に直接依存するのではなく、インターフェース(抽象)を介してやり取りするようにします。

直接依存する:

class Service {
  printer = new InkPrinter()
}

インターフェースが仲介する:

interface Printer {
  print(): void
}
// 実体
class InkPrinter implements Printer {
  print() {
    console.log("インクで印刷")
  }
}
// Service
class Service {
  constructor(private printer: Printer) {}

  run() {
    this.printer.print()
  }
}
9
8
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
9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?