Single Responsibility Principle
クラスを変更する理由はひとつ以上存在してはならない
クラスをどう定義するか、の原則です。
SOLID原則と呼ばれる、主要5原則のひとつ。
- Single Responsibility Principle 単一責任原則
- Open Closed Principle 開放閉鎖原則
- Liskov Substitution Principle リスコフ置換原則
- Interface Segregation Principle インターフェース分離原則
- Dependency Inversion Principle 依存性逆転原則
どんなときに使える?
メソッド数が恐ろしく多いとき
〇〇クラス、メソッド5個分の実装終わりましたー
〇〇クラスのメソッド10個追加完了っす!
なんかこのクラス、メソッド100個くらいあるんだけど…
プログラム開発をしていると、やたら大きいクラスが登場したりします。メソッドがなん十個もあったり、1万行くらいのコードになったり…
こういうのはたいてい、あまり深い考えなしに、主力クラスに処理をぶちこんでいる状態。
クラスを分けておかないと、今後の開発で支障が出そうです。でもどう分ければいいんでしょう?
クラス分割の指針=変更理由はひとつだけ
単一責任原則は、クラスをどう分割するかの指針になります。
それは「そのクラスの変更理由が1つだけ」になるよう、分割することです。
具体的な例で考えましょう。
従業員クラスの例
従業員のデータを管理するクラスを考えます。
// 従業員クラス
public class Employee {
// 給与計算を行う
public Money calculatePay() {
...
}
// 作業時間報告書を出力する
public String reportHours() {
...
}
// データベースに保管する
public void save() {
...
}
}
このクラスには3つのメソッドがあります。
- calculatePay 従業員の給与を計算します
- reportHours 従業員の作業報告書を出力する
- save 従業員に関する情報をデータベースに保管します
いずれも従業員のデータを操作するメソッドなので、従業員クラスにあるのが望ましい…本当でしょうか?
変更理由が多いクラス
問題なのは、3つのメソッドが、まったく違った理由で変更される可能性があることです。
- calculatePay 給与計算に関わるルールが変わったら、修正が必要
- reportHours レポートのフォーマットが変わったら、修正が必要
- save データベーススキーマが変わったら、修正が必要
これら3つの変更は、おそらく別々の理由、別々のタイミングで、発生するでしょう。
そしてどの変更であっても、従業員クラスの変更が必要になってしまいます。
変更するということは、動かなくなる可能性がある、ということです。できるだけ1つのクラスは、変更機会が少ないほうがよいのです。
従業員クラスを分割する
では、クラスをどう分割すればよいでしょう?
そう、変更される可能性ごとに分けるのです。
// 従業員クラス
public class Employee {
// 給与計算を行う
public Money calculatePay() {
...
}
}
// 従業員報告書クラス
public class EmployeeReporter {
// 作業時間報告書を出力する
public String reportHours(Employee e) {
...
}
}
// 従業員リポジトリクラス
public class EmployeeRepository {
// データベースに保管する
public void save(Employee e) {
...
}
}
これで、変更理由に対する改修箇所は、こうなります。
- 給与計算に関わるルールが変わったら……従業員クラスを修正
- レポートのフォーマットが変わったら……従業員報告書クラスを修正
- ベーススキーマが変わったら……従業員リポジトリクラスを修正
各々のクラスで、変更理由がひとつになりました。
それぞれのクラスの責任が単一となったわけです。
責任分割を考えるコツ=何に責任を持たないか?
単一責任とはいっても、「責任」をどう考えるのかは、とても難しいです。人により言うことは変わるし、状況によっても捉え方は違います。
単一責任について、ひとつ考えかたのコツがあります。
責任を考えるとき、逆に
と考えるのです。
従業員クラスは、
- 報告書フォーマットに依存しないし
- DBのスキーマも決めるべきでない
であるなら、その処理は従業員クラスに実装すべきではないのです。
まとめ
単一責任原則の本質は、クラスだけでなく、関数やメソッドといったあらゆる要素で同じです。
「変更する理由は、ただひとつだけ」。その定義はとても難しいです。
でも、ちゃんと「変更する理由」を考えて、誰かに説明できるようにしておきましょう。それでクラスの責任はかなりクリアになるはずです。
- Single Responsibility Principle 単一責任原則
- Open Closed Principle 開放閉鎖原則
- Liskov Substitution Principle リスコフ置換原則
- Interface Segregation Principle インターフェース分離原則
- Dependency Inversion Principle 依存性逆転原則