概要
オブジェクト指向設計において最も基本でありながら、
最も乱用される原則が SRP(Single Responsibility Principle) ― 単一責任の原則 ― である。
多くの人がこの原則を「クラスに1つのことだけやらせる」と理解しているが、
現実の設計において「1つのこと」とは何か?「責任」とは何か?
それをどうやってコード構造に落とし込むのか?
本稿ではこのSRPを、形式ではなく設計思考の核として再解釈し、
具体的なパターンと判断基準を持った実践的な技法として解き明かす。
1. SRPの正体:責任とは「理由の単位」
「あるクラスが変更される理由が1つだけであるべき」
(by Robert C. Martin)
つまり「1つの責任」とは、1つの関心領域 or ユースケース
1つの理由でしか変更が起きない構造を意味する。
2. よくあるSRP違反の構造
class Report {
constructor(private data: string) {}
format(): string {
return `Report: ${this.data}`
}
print(): void {
console.log(this.format())
}
saveToFile(): void {
// ファイル保存処理...
}
}
- フォーマットの責任
- **表示(出力)**の責任
- **永続化(保存)**の責任
→ 3つの異なる関心が混在している
→ 変更理由が3つ以上存在してしまう
3. 正しい責任分離の例
class Report {
constructor(private readonly data: string) {}
format(): string {
return `Report: ${this.data}`
}
}
class ReportPrinter {
print(report: Report): void {
console.log(report.format())
}
}
class ReportSaver {
save(report: Report): void {
// ファイル保存処理...
}
}
-
Report
は「データとフォーマット」のみ - 出力や保存は外部に委譲
- それぞれのクラスは単一の理由でのみ変更される
4. 「SRP = 分けすぎ」ではない
- SRPは「なんでもクラスに分けろ」ではない
- 責任が衝突しないなら統合してもよい
- 分ける基準は以下:
5. SRP分離判断フロー
① このクラスは「異なる視点」から変更されうるか? → YES → SRP違反
② このロジックは「別の専門領域」に関係しているか? → YES → 分離を検討
③ 仕様変更がこのクラスに波及しやすいか? → YES → 責任が混在している可能性
④ 単体テストでモックや依存を過剰に用いているか? → YES → SRP不完全の兆候
6. 実務での応用設計パターン
✅ ViewModelとServiceの責任分離(フロントエンド)
- ViewModel → UIロジック(状態変化とイベント管理)
- Service → データ取得や永続化(API通信)
→ ViewModelがAPI通信まで背負うと変更理由が爆発する
✅ 集約とユースケースの責任分離(ドメイン設計)
- ドメインモデル(Entity, VO) → ビジネスルールを保持
- ユースケース → 操作の流れを管理
→ 同じクラスで両方を扱うと設計が肥大化しやすい
よくある誤解と対策
❌ SRPを意識しすぎて逆に複雑化する
→ ✅ 「小さく分ける」ではなく、「変更理由ごとに分ける」
❌ UI、永続化、ビジネスルールが同居しても動けばOK
→ ✅ それは可読性とテスト性、変更耐性を犠牲にしている
❌ 分けることが目的化してしまう
→ ✅ SRPの目的は「将来の拡張や変更の影響を局所化すること」
結語
SRPは「クラスを小さく保つためのルール」ではない。
それは「ソフトウェアを局所的に理解可能で、拡張に強い構造にするための設計原理」である。
- 責任を分離することで、変更の波及を防ぎ
- ロジックを文脈で整理し、テスト性・可読性・安全性を高め
- 結果として、進化可能な構造を手に入れる
オブジェクト指向におけるSRPとは、
“意味の分離によって変更を制御し、秩序ある設計へ導く構造的ガイドラインである。”