インターフェース分離の原則

SOLID の I - クライアントが使わないメソッドへの依存を強制しない原則

SOLID設計原則

インターフェース分離の原則とは

インターフェース分離の原則 (Interface Segregation Principle, ISP) は、SOLID 原則の I にあたる設計原則で、「クライアントが使わないメソッドへの依存を強制してはならない」。大きなインターフェースを小さな専用インターフェースに分割する。

違反の例

// ❌ ISP 違反: 巨大なインターフェース
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
  attendMeeting(): void;
}

// ロボットは eat() と sleep() を実装できない
class Robot implements Worker {
  work() { /* OK */ }
  eat() { throw new Error('Robots do not eat'); }  // ❌ 無意味な実装
  sleep() { throw new Error('Robots do not sleep'); }
  attendMeeting() { /* OK */ }
}

準拠の例

// ✅ ISP 準拠: 小さなインターフェースに分割
interface Workable { work(): void; }
interface Eatable { eat(): void; }
interface Sleepable { sleep(): void; }
interface MeetingAttendable { attendMeeting(): void; }

// 人間: 全て実装
class Human implements Workable, Eatable, Sleepable, MeetingAttendable {
  work() { /* ... */ }
  eat() { /* ... */ }
  sleep() { /* ... */ }
  attendMeeting() { /* ... */ }
}

// ロボット: 必要なものだけ実装
class Robot implements Workable, MeetingAttendable {
  work() { /* ... */ }
  attendMeeting() { /* ... */ }
}

TypeScript での実践

// ❌ ISP 違反: Repository が読み書き両方を強制
interface Repository<T> {
  findById(id: string): Promise<T | null>;
  findAll(): Promise<T[]>;
  save(entity: T): Promise<void>;
  delete(id: string): Promise<void>;
}

// 読み取り専用のサービスに save/delete は不要

// ✅ ISP 準拠: 読み取りと書き込みを分離
interface ReadRepository<T> {
  findById(id: string): Promise<T | null>;
  findAll(): Promise<T[]>;
}

interface WriteRepository<T> {
  save(entity: T): Promise<void>;
  delete(id: string): Promise<void>;
}

// 読み取り専用サービス
class ReportService {
  constructor(private repo: ReadRepository<Order>) {}
  async getReport() { return this.repo.findAll(); }
}

ISP 違反のサイン

  • インターフェースのメソッドを空実装や throw で埋めている
  • インターフェースの一部のメソッドしか使わないクライアントが多い
  • インターフェースの変更が無関係なクライアントに影響する

他の SOLID 原則との関係

原則 ISP との関係
単一責任 (SRP) クラスの責務を分離 → インターフェースも分離
リスコフ置換 (LSP) 小さなインターフェースなら置換が容易
依存性逆転 (DIP) 小さなインターフェースに依存 → 疎結合

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

関連用語