SOLID原則とは?オブジェクト指向設計の基本を解説

プログラミング言語

SOLID原則は、オブジェクト指向設計の5つの原則をまとめたものです。読みやすく・拡張しやすく・テストしやすいコードを書くための指針として、長く支持されてきました。本記事では、SOLID原則を具体例つきで解説します。

SOLIDの5原則

  • S:Single Responsibility(単一責任)
  • O:Open/Closed(開放/閉鎖)
  • L:Liskov Substitution(リスコフ置換)
  • I:Interface Segregation(インターフェース分離)
  • D:Dependency Inversion(依存関係逆転)

S: 単一責任の原則

「クラスは変更理由を1つだけ持つべき」。複数の関心事を抱えたクラスは、変更の度に副作用が増えます。

// NG:保存とメール送信の両方を担う
class User {
  save() { db.save(this); this.sendEmail(); }
}

// OK:責務を分離
class UserRepository { save(u) { db.save(u); } }
class UserMailer { sendWelcome(u) { /* ... */ } }

O: 開放/閉鎖の原則

「拡張に対して開いていて、修正に対して閉じている」。新機能追加で既存コードを書き換えなくて済む設計。

// NG:if文で型分岐
function area(shape) {
  if (shape.kind === 'circle') return Math.PI * shape.r ** 2;
  if (shape.kind === 'square') return shape.side ** 2;
}

// OK:多態で拡張可能
class Shape { area() { throw new Error('override'); } }
class Circle extends Shape { area() { return Math.PI * this.r ** 2; } }

L: リスコフ置換原則

「派生型は基底型と置き換え可能であるべき」。継承関係でサブクラスがスーパークラスの契約を破ると違反です。

class Bird { fly() {} }
// NG:飛べないペンギンがBirdを継承
class Penguin extends Bird { fly() { throw 'cannot fly'; } }
// OK:Flyable / Walkable などインターフェースを分離

I: インターフェース分離

「クライアントは使わないメソッドに依存させない」。1つの巨大なインターフェースより、小さな複数の方が良い。

// NG:印刷もスキャンもFAXも全部できるI/F
// OK:Printable / Scannable / Faxable に分割

D: 依存関係逆転

「上位モジュールは下位モジュールに依存してはいけない。両者は抽象に依存すべき」。具体クラスではなくインターフェースに依存させ、テストや差し替えを容易に。

// NG
class OrderService {
  constructor() { this.db = new MySQLClient(); }
}

// OK:抽象(インターフェース)を受け取る
class OrderService {
  constructor(repo) { this.repo = repo; }
}

適用のコツ

  • 「全てに当てはめる」より「変更が辛い時の指針」として使う
  • テストが書きやすくなったらSOLID的に良いサイン
  • 関数型・モジュール型コードでも本質は同じ

まとめ

SOLID原則は、長期保守を意識する設計の共通言語です。教科書的に全部当てはめる必要はありませんが、コードが辛くなった時に立ち戻る指針として強力。リファクタリングの判断軸として身につけておきましょう。