SRP(単一責任の原則)とは|具体例とSRP違反・やりすぎ(過剰分割)の見極めをわかりやすく解説

SRP(単一責任の原則/Single Responsibility Principle)とは、1つのクラスやモジュールが持つ責任を1つに絞るという設計指針で、SOLID原則の最初の「S」にあたります。Robert C. Martinが提唱し、古典的には「クラスが変更される理由は1つだけであるべき」と表現されます。責任を分けておくと、ある変更が他の機能に波及しにくくなり、保守性とテストのしやすさが上がります。本記事ではまず、SRPの定義と具体例、SRP違反の見分け方、やりすぎ(過剰分割)の判断を整理します。そのうえで、実装例や他のSOLID原則との関係を詳しく解説します。

まとめ:SRPの要点とSRP違反のサイン

先に要点を示します。SRPは「1つのクラスに変更理由を1つだけ」にする原則です。書籍『Clean Architecture』(2017年)でMartinは、責任を「ただ1人のアクター(利害関係者)に対する責任」とより厳密に定義しました。下表は、設計を見直すべきSRP違反の典型的なサインです。

サイン 示すこと 対処
変更理由が複数 役割の混在 責任ごとに分割
1クラスが肥大 多責任の兆候 クラス抽出
修正が広く波及 高い結合度 境界を再設計

たとえば、業務ロジック・データベース保存・通知メール送信を1つのクラスが抱えていると、保存方法の変更でも通知仕様の変更でも同じクラスを触ることになります。これは変更理由が3つある状態で、SRP違反のわかりやすい例です。責任ごとにクラスを分ければ、変更の影響範囲が狭まります。ただし分けすぎるとクラスが増えて見通しが悪くなるため、過剰分割にも注意が必要です。以降で、定義の背景や実装例を詳しく見ていきます。

単一責任の原則の定義|「変更理由は1つ」と「1人のアクター」

SRPには表現の異なる2つの定義があります。1つ目は古典的な「クラスが変更される理由は1つだけであるべき」という言い回しで、Robert C. Martinの『アジャイルソフトウェア開発の奥義』(2002年)や『Clean Code』(2008年)で広まりました。読み方は「エスアールピー」で、日本語では単一責任の原則、まれに単一責務の原則とも訳されます。

2つ目は『Clean Architecture』(2017年)での精緻化で、「モジュールはただ1人のアクター(利害関係者)に対して責任を負うべきである」という定義です。ここでの「責任」は、変更を求めてくる人(アクター)を単位に考えます。同じコードでも、経理部門と人事部門という別々のアクターの都合で変更が入るなら、それは2つの責任を抱えていると判断します。「1つの仕事しかしない」ではなく「変更を要求する相手が1人か」で見るのが、実務では扱いやすい基準です。SRPはSOLID(SRP・OCP・LSP・ISP・DIP)の出発点で、残る4原則の土台になります。

SRP違反の具体例とリファクタリング(コード)

典型的なSRP違反は、1つのクラスが検証・永続化・通知など複数の関心事を抱えるケースです。次の UserService は、入力検証・DB保存・確認メール送信の3つを1つのメソッドで行っています。

class UserService {
  register(user) {
    if (!user.email) throw new Error("invalid");  // 検証
    this.db.save(user);                           // 永続化
    this.mailer.send(user.email, "ようこそ");      // 通知
  }
}

このクラスは「検証ルールの変更」「保存先の変更(DBから外部APIへ等)」「メール文面の変更」という3つの異なる理由で修正され、変更理由が3つある=SRP違反です。関心事ごとにクラスを分けると、それぞれが単一の責任を持ちます。

class UserValidator { validate(user) { if (!user.email) throw new Error("invalid"); } }
class UserRepository { save(user) { /* DBやAPIへ保存 */ } }
class WelcomeMailer { send(user) { /* 確認メール送信 */ } }

class UserService {
  constructor(validator, repository, mailer) {
    this.validator = validator; this.repository = repository; this.mailer = mailer;
  }
  register(user) {
    this.validator.validate(user);
    this.repository.save(user);
    this.mailer.send(user);
  }
}

分割後、保存先を変えたいときは UserRepository だけを、メール文面を変えたいときは WelcomeMailer だけを修正します。UserService は「登録の流れを組み立てる」責任だけを持ち、各部品を受け取って呼び出すオーケストレーション役に徹します。テストも、検証・保存・通知を個別に差し替えて検証でき、単体テストが書きやすくなります。

SRP違反を見分ける4つのサイン

コードを見てSRP違反を疑う手がかりは、次の4つです。

  • 変更理由が複数挙がる:「このクラスはなぜ変更されるか」を列挙して2つ以上出るなら、責任が混ざっています。
  • クラスやメソッドが肥大化している:数百行のクラスや、多数のフィールドを持つ「Godクラス」は多責任の典型です。
  • 1か所の修正が無関係な機能へ波及する:表示の変更でデータ処理まで壊れるなら、関心事が結合しています。
  • 説明に「〜と〜」が必要:クラスの役割を1文で言うのに「AとBをする」と接続詞が要るなら、責任が2つに分かれているサインです。

これらは絶対的な違反判定ではなく、設計を見直すきっかけの目安です。実際に一緒に変更される処理かどうかを合わせて判断します。

やりすぎ(過剰分割)を避ける粒度の判断

SRPは分割そのものが目的ではありません。責任を細かく分けすぎると、クラスやファイルが増えて全体像が追いにくくなり、かえって保守しづらくなります。過剰分割を避ける判断基準は「同じ理由で一緒に変わるものは1か所にまとめる」ことです。常に同時に変更される処理を、原則の名のもとに無理やり別クラスへ切り出す必要はありません。

粒度を決めるときは、凝集度(関連する処理がまとまっているか)とアクターの視点を使います。1つのアクターの都合だけで変わる処理は、多少行数があっても1つのクラスにまとめてよい、と考えると迷いにくくなります。逆に、複数のアクターの要求が同居している場合に初めて分割を検討します。将来の変更を過剰に想定して先回りで分けるのは、YAGNI(今必要ないものは作らない)に反し、複雑さだけが増えがちです。

SRPを「アクター」で捉える|給与計算クラスの例

『Clean Architecture』でMartinが挙げた有名な例が、給与計算の Employee クラスです。このクラスに、勤務時間を集計する calculatePay()(経理部門が仕様を決める)、労働時間を報告する reportHours()(人事部門が仕様を決める)、レコードを保存する save()(データベース管理者が仕様を決める)が同居しているとします。

3つのメソッドは別々のアクター(経理・人事・DBA)に対して責任を負っており、あるアクターの要求で共有ロジックを変えると、別のアクターの機能を意図せず壊す危険があります。これがアクターの観点で見たSRP違反です。対処は、アクターごとに処理を別クラスへ分け、共有していたロジックの結合を断つことです。「何をするか」ではなく「誰のために変わるか」で境界を引くと、現実の変更に強い設計になります。

他のSOLID原則との関係

SRPはSOLIDの土台で、残るO・L・I・Dを適用しやすくします。責任が1つに絞られていれば、機能追加時に既存コードを変更せず拡張するオープンクローズドの原則(OCP)を適用しやすくなります。また、責任の境界が明確だと、抽象(インターフェース)に依存させて具体を差し替える依存性逆転の原則(DIP)との相性もよくなります。責任を実装パターンへ落とし込む際は、GoFのデザインパターン(StrategyやObserverなど)が具体的な分割の型として役立ちます。5つの原則は単独で使うより、互いに補完させると効果が高まります。

よくある質問

SRP(単一責任の原則)とは何ですか?わかりやすく教えてください

SRPは、1つのクラスやモジュールが負う責任を1つに絞る設計原則です。古典的には「クラスが変更される理由は1つだけであるべき」と表現します。たとえば「データの計算」と「画面表示」を同じクラスに混ぜると、どちらの都合でもそのクラスを変更することになり、影響範囲が広がります。責任を1つにしておけば、変更が局所化されて保守やテストが楽になります。SOLID原則の出発点となる考え方です。

SRPの具体例(プログラミング)を教えてください

よくある例は、ユーザー情報を扱うクラスが「入力値の検証」「データベースへの保存」「確認メールの送信」をすべて担っているケースです。この状態では、保存先をDBからAPIに変える変更でも、メール文面を変える変更でも、同じクラスを修正する必要があります。SRPに沿って、検証・永続化・通知をそれぞれ別クラスに分けると、各クラスは1つの変更理由だけを持ち、修正の波及が抑えられます。これが責任の分離の基本形です。

SRP違反とは何ですか?どう見分けて直しますか?

SRP違反は、1つのクラスが複数の変更理由(責任)を抱えている状態です。見分け方の目安は、「このクラスはなぜ変更されるか」を挙げてみて理由が2つ以上あること、クラスが肥大化していること、1か所の修正が無関係に見える機能まで波及することです。直すときは、混在している関心ごとを別クラスへ抽出し、それぞれが単一の責任を持つように境界を引き直します。オープンクローズドの原則など他のSOLID原則と組み合わせると効果が高まります。

SRPはやりすぎると問題ですか?過剰分割の見極めは?

やりすぎると逆効果になります。責任を細かく分けすぎると、クラスやファイルが増えて全体像が追いにくくなり、かえって保守しづらくなります。見極めの目安は「同じ理由で一緒に変わるものは1か所にまとめる」ことです。常に一緒に変更される処理を無理に分割する必要はありません。SRPの目的は分割そのものではなく、変更理由を整理して影響を局所化することです。粒度は、変更が実際にどうまとまるかを基準に判断します。

SRPと他のSOLID原則の関係は?

SRPはSOLID原則の「S」で、残るO・L・I・Dの土台になります。責任を1つに絞っておくと、機能拡張時に既存コードを壊しにくくするオープンクローズドの原則や、抽象に依存させる依存性逆転の原則が適用しやすくなります。SRPで責任の境界を明確にすることが、他の原則を活かす前提になります。5つの原則は単独でなく、互いに補完し合います。

ビジネスで見る「SRP」と単一責任の原則は同じですか?

同じ略語ですが、文脈によって指すものが異なります。本記事のSRPはソフトウェア設計のSingle Responsibility Principle(単一責任の原則)です。ビジネスの文脈では「SRP」が別の用語の略として使われることもあるため、検索結果が技術記事かビジネス記事かで意味が変わります。プログラミングやクラス設計、SOLIDの話題で出てくるSRPは、ここで説明している単一責任の原則を指していると整理してください。

関連記事

資料請求

RELATED POSTS 関連記事