December 15, 2025 (3mo ago) — last updated March 19, 2026 (25d ago)

インターフェース分離の原則(ISP)ガイド

ISP を TypeScript と React の実践例で学び、肥大化したインターフェースを分割して可読性・保守性・テスト容易性を向上させる方法を解説します。

← Back to blog
Cover Image for インターフェース分離の原則(ISP)ガイド

インターフェース分離の原則(ISP)は、クライアントが使わないメソッドに依存させないことでコードの結合度を下げ、保守性とテスト容易性を高めます。本記事では TypeScript と React の実践例を使って、肥大化したインターフェースの見つけ方、段階的なリファクタ手順、チーム導入のベストプラクティスをわかりやすく解説します。

インターフェース分離の原則(ISP) - TypeScript & React

要約: インターフェース分離の原則(ISP)を実践で学び、TypeScript と React の具体例で肥大化したインターフェースを分割して可読性・保守性・テスト容易性を向上させます。

はじめに

インターフェース分離の原則(ISP)は、クライアントが使わないメソッドに依存させてはならない、という考え方です。適切に適用すれば、ISP は結合度を下げ、テストを簡素化し、コードの変更や理解を容易にします。本ガイドでは、実践的な TypeScript と React の例を用いて、今日から使えるリファクタ手順とチーム導入の方法を示します。SOLID の他の原則や詳細な設計ガイドについては、社内の SOLID ガイド や TypeScript のドキュメントも参照してください。


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

マルチツールの肥大化したインターフェースと、フォーカスされたインターフェースとしての単一ドライバーを比較する図。

90 個のボタンが付いたリモコンで「再生」だけを探す苛立ちを想像してください。それは、クラスが大きく焦点の定まらないインターフェースを実装しなければならないソフトウェアとよく似ています。SOLID の「I」である ISP は、何でもこなす 1 つの契約ではなく、小さくクライアント固有のインターフェースを設計することを勧めます。これにより「ファットインターフェース」や「ゴッドインターフェース」と呼ばれるアンチパターンを避け、より明確で柔軟なシステムになります1

なぜ肥大化したインターフェースは問題か

  • 不要な依存: クラスが呼び出さないメソッドに結合され、無関係な変更が危険になる。
  • 認知負荷: 開発者が必要なメソッドを見つけるために無関係なメソッドを読み解かなければならない。
  • 空の実装: クラスが使わないメソッドをスタブ化せざるを得ず、ボイラープレートが増える。

コアとなる考えは単純です: 各クライアントに必要なものだけを与え、それ以上は与えない。これによりコンポーネントは理解しやすく、テストしやすく、進化させやすくなります。

モノリシックなインターフェース vs 分離されたインターフェース

並べて比較するとトレードオフが明確になります。

属性モノリシックなインターフェース(アンチパターン)分離されたインターフェース(ISP)
結合度高い; クラスが使わないメソッドに依存する。低い; クライアントは必要なメソッドのみに依存する。
凝集性低い; 無関係なメソッドがまとめられている。高い; 各インターフェースは単一の役割を果たす。
保守性困難; 小さな変更が広く波及する。容易; 変更は関連するクライアントのみに影響する。
テスト容易性難しい; モックが大きく壊れやすくなる。容易; 小さなインターフェースはモックが簡単。

ISP を採用することで、機能追加やリファクタリング時にもコードベースを柔軟に保てます。

実践における ISP:よくある違反

違反はアプリを直ちに壊すわけではありませんが、保守を困難にします。空のメソッドが多いクラスや、多数のオプショナル props を持つコンポーネントを探してください。

管理者と閲覧者の役割がさまざまなアイコンと相互作用する「ゴッドインターフェース」の概念図。

TypeScript における典型的な「ゴッドインターフェース」

// ANTI-PATTERN: A "fat interface" that violates ISP
interface IUserActions {
  createUser(data: UserData): void;
  editUser(id: string, data: UserData): void;
  deleteUser(id: string): void;
  viewUserProfile(id: string): UserProfile;
  changeUserRole(id: string, newRole: Role): void;
  publishArticle(article: Article): void;
  approveComment(commentId: string): void;
}

このインターフェースを実装せざるを得ない Viewer クラスは、意味のないメソッドやエラーを投げるメソッドを含むことになり、保守コストとリスクが増大します。

React における肥大化したコンポーネント props

フロントエンドでよくある症状は、多くのオプショナル props を持つ汎用的な Card です。これにより、許容される props の組み合わせが曖昧になり、条件分岐レンダリングが複雑になります。

// ANTI-PATTERN: A bloated props interface
interface CardProps {
  title: string;
  description?: string;
  imageUrl?: string;
  imageAltText?: string;
  videoUrl?: string;
  authorName?: string;
  authorAvatarUrl?: string;
  publicationDate?: string;
  articleLink?: string;
  onClick?: () => void;
  // ... and many more optional props
}

これらのアンチパターンは、以下のリファクタリング手順に向けた準備になります。

リファクタリング手順:モノリシックから分離へ

ISP へのリファクタリングは大規模な書き換えを必要としません。責務を分割し契約を明確にするために、小さなフォーカスされた変更を使って進めます。

「I user Actions」を「Editor」と「Viewer」固有のインターフェースにリファクタリングする手描き図。

1) TypeScript における「ゴッドインターフェース」の修正

ステップ A: クライアントの役割を特定します。例: 管理者(Admins)、編集者(Editors)、閲覧者(Viewers)。

ステップ B: 役割固有のインターフェースを作成します。

// GOOD: Focused interfaces
interface IViewerActions {
  viewUserProfile(id: string): UserProfile;
}

interface IEditorActions {
  publishArticle(article: Article): void;
  approveComment(commentId: string): void;
}

interface IAdminActions {
  createUser(data: UserData): void;
  editUser(id: string, data: UserData): void;
  deleteUser(id: string): void;
  changeUserRole(id: string, newRole: Role): void;
}

ステップ C: 必要なものだけを実装します。

class Viewer implements IViewerActions {
  viewUserProfile(id: string): UserProfile {
    console.log(`Fetching profile for user ${id}...`);
    // ... fetch and return profile
  }
}

管理者は必要に応じて複数のインターフェースを実装できます。この分離により不要な再コンパイルが減り、各機能の所有者が明確になります。

2) 判別共用体(discriminated unions)で肥大化した React props をリファクタリング

判別共用体を使うことで各コンポーネントバリアントは明確な契約を持ちます。TypeScript のユニオン型と識別子はこれに最適です。詳しい型の使い方は公式ドキュメントも参照してください2

// GOOD: Base props
type BaseCardProps = {
  title: string;
  onClick?: () => void;
};

// GOOD: Specific variants
type ImageCardProps = BaseCardProps & {
  cardType: 'image';
  imageUrl: string;
  imageAltText: string;
};

type ArticleCardProps = BaseCardProps & {
  cardType: 'article';
  description: string;
  authorName: string;
  articleLink: string;
};

type CardProps = ImageCardProps | ArticleCardProps;

このパターンにより、エディタが有効な props の組み合わせを即座に強制し、不正な組み合わせを防ぎます。社内のコンポーネント設計ガイドや TypeScript の判別共用体ページ を参照し、コンポーネントのバリアント設計を標準化しましょう。

チーム全体で ISP を監査・適用する

一度きりの修正は有益ですが、チームの慣行に ISP を組み込むことで持続的な価値が生まれます。明確な監査基準と自動チェックを組み合わせてインターフェースの健全性を維持しましょう。

明確な監査基準

レビュープロセス中に ISP 違反を見つけるための共有チェックリストを作成します。例としてのレッドフラグ:

  • メソッドが多いインターフェース(5〜7 を超えたら点検に値する)。
  • 実装に空のメソッドやスタブ化されたメソッドがある。
  • 「Manager」や「Handler」など曖昧な名前のインターフェース。
  • 多数のオプショナルフィールドを持つコンポーネント props。

一般的なアプローチは、手動レビューと自動ツールを組み合わせて適用範囲を拡大することです。

自動化による適用

ESLint とカスタムルールは、オーバーサイズの props やインターフェースを実装しているがメソッドが未実装のクラスのような直接的なパターンを検出するのに強力です3。また、AI コーディングアシスタントはインターフェースと実装をレビューするよう促すと設計上の臭い(design smells)を指摘するのに役立ちます4

自動チェックと人的レビューの両方が価値を持ちます: ツールは一貫性と速度を提供し、人は文脈とビジネス意図を捉えます。

項目手動監査自動適用
正確さ文脈的な問題には高い定義されたルールには一貫性がある
一貫性レビュアーにより差があるどこでも同じルールが適用される
速度大規模コードベースでは遅い速く、IDE/CI に統合できる

両方を使いましょう: 自動化でルーチンチェックを処理し、レビュアーは微妙な設計判断に注力します。この組み合わせは TDD ワークフローを改善します。小さなインターフェースはモックやテストが簡単で、Red-Green-Refactor サイクルを高速にし、単体テストを明確にします5

ISP と AI 駆動開発

AI が開発ワークフローにますます組み込まれるにつれ、明確なインターフェースは GPT 系モデルのようなツールがコードを推論するのに役立ちます。小さくフォーカスされたインターフェースは曖昧さを減らし、生成されるコードや自動リファクタ提案、ドキュメントの品質を向上させます4

クリーンなインターフェースは、人間と機械の双方にとって正確な指示書のように機能します。その明確さは推測を減らし、より正確で信頼できる AI 支援の変更をもたらします。

よくある ISP に関する質問(簡潔版)

ISP はすべてのメソッドに個別のインターフェースを必要とするのか?

いいえ。目標はどこでも一メソッドのインターフェースを作ることではありません。同じクライアントによって常に一緒に使われるメソッドはまとめてください。目標はフォーカスされ凝集性のあるインターフェースであって、断片化ではありません。

ISP は単一責任の原則(SRP)とどう違うのか?

SRP はクラスに適用され、クラスは変更理由を一つだけ持つべきだと言います。ISP はインターフェースに適用され、クライアントは使わないメソッドに依存すべきではないと言います。クラスが肥大化したインターフェースを実装すると、SRP に従っていても ISP を破る可能性があります。

いつ ISP を無視してよいか?

主に変更できないインターフェース、つまり第三者ライブラリやあなたの管理外のレガシー API の場合です。また、すべてのクライアントが本当にすべてのメソッドを必要とする場合は、大きめのインターフェースでも凝集性があり許容されます。


クイックリファクタチェックリスト

  • 各インターフェースの役割とクライアントを特定する。
  • 大きなインターフェースを役割固有の契約に分割する。
  • 変種コンポーネントの props には TypeScript の判別共用体を使用する2
  • オーバーサイズのインターフェースや props をフラグする lint ルールを追加する3
  • 自動チェックとコードレビューを組み合わせて微妙な判断を行う。

簡潔な Q&A(開発者からのよくある質問)

Q: ISP 違反を素早く見つける方法は?

A: 多くの無関係なメソッドを持つインターフェース、空の実装や例外を投げる実装、何十ものオプショナル props を持つコンポーネントを探してください。これらは契約を分割すべき強いサインです。

Q: 「ファットインターフェース」を直す最速の方法は?

A: 明確なクライアント役割を特定し、フォーカスされた役割固有のインターフェースを作成し、実装を必要なものだけに更新します。段階的に変更して影響を最小限に抑えましょう。

Q: 成長するコードベースで ISP の後退を防ぐには?

A: オーバーサイズのインターフェースを検出する lint ルールや CI チェックを追加し、インターフェース設計のレビューチェックリストを導入してください。自動化と定期的な手動監査を組み合わせましょう。


At Clean Code Guy, we help teams apply principles like ISP to build maintainable, AI-ready codebases. Get a free codebase audit to find hidden issues and improve long-term maintainability.

1.
Robert C. Martin (“Uncle Bob”), SOLID 原則の概要。 https://blog.cleancoder.com/
2.
TypeScript ハンドブック — 判別共用体と高度な型。 https://www.typescriptlang.org/docs/handbook/2/advanced-types.html#discriminated-unions
3.
ESLint のドキュメントとカスタムルールに関するガイド。 https://eslint.org/docs/latest/
4.
GitHub Copilot と AI コーディングアシスタント — 事例とガイダンス。 https://github.com/features/copilot
5.
Martin Fowler — リファクタリングとテストの実践。 https://martinfowler.com/
6.
State of JS — フロントエンドツールの採用傾向と満足度。TypeScript の採用と満足度が高い傾向にある調査結果を参照してください。 https://stateofjs.com/
← Back to blog
🙋🏻‍♂️

AIがコードを書きます。
あなたがそれを長持ちさせます。

AI加速の時代において、クリーンコードは単なる良い実践ではありません—スケールするシステムと自らの重みで崩壊するコードベースの違いです。