インターフェース分離の原則
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) | 小さなインターフェースに依存 → 疎結合 |
さらに掘り下げるなら関連書籍が参考になる。