Single Responsibility Principle
クラスを変更する理由はひとつ以上存在してはならない
クラスをどう定義するか、の原則です。
SOLID原則と呼ばれる、主要5原則のひとつ。
- Single Responsibility Principle 単一責任原則
- Open Closed Principle 開放閉鎖原則
- Liskov Substitution Principle リスコフ置換原則
- Interface Segregation Principle インターフェース分離原則
- Dependency Inversion Principle 依存性逆転原則
どんなときに使える?
メソッド数が恐ろしく多いとき
![](http://plainprogram.com/wp-content/uploads/2023/05/0_4_1_0_16_17_0_20_531_0_0_3_p1-150x150.png)
〇〇クラス、メソッド5個分の実装終わりましたー
![](http://plainprogram.com/wp-content/uploads/2023/05/0_4_6_0_44_118_56_88_92_0_0_26_p1-150x150.png)
![](http://plainprogram.com/wp-content/uploads/2023/05/0_4_6_0_44_118_56_88_92_0_0_26_p1-150x150.png)
![](http://plainprogram.com/wp-content/uploads/2023/05/0_4_6_0_44_118_56_88_92_0_0_26_p1-150x150.png)
〇〇クラスのメソッド10個追加完了っす!
![](http://plainprogram.com/wp-content/uploads/2023/05/0_54_21_0_409_12_0_1_58_0_0_16_p6_2-150x150.png)
![](http://plainprogram.com/wp-content/uploads/2023/05/0_54_21_0_409_12_0_1_58_0_0_16_p6_2-150x150.png)
![](http://plainprogram.com/wp-content/uploads/2023/05/0_54_21_0_409_12_0_1_58_0_0_16_p6_2-150x150.png)
なんかこのクラス、メソッド100個くらいあるんだけど…
プログラム開発のとき、なんだかやたら大きいクラスができたりします。メソッドがなん十個もあったり、1万行くらいのコードになったり…
クラスを分けておかないと、今後の開発で支障が出そうです。でもどう分ければいいんでしょう?
クラス分割の指針=変更理由はひとつだけ
単一責任原則は、クラスをどう分割するかの指針になります。
それは「そのクラスの変更理由が1つだけ」になるよう、分割することです。
どういうこと?具体的な例で考えましょう。
従業員クラスの例
従業員のデータを管理するクラスを考えます。
// 従業員クラス
public class Employee {
// 給与計算を行う
public Money calculatePay() {
...
}
// 作業時間報告書を出力する
public String reportHours() {
...
}
// データベースに保管する
public void save() {
...
}
}
このクラスには3つのメソッドがあります。
- calculatePay 従業員の給与を計算します
- reportHours 従業員の作業報告書を出力する
- save 従業員に関する情報をデータベースに保管します
いずれも従業員のデータを操作するメソッドなので、従業員クラスにあるのが望ましい…本当でしょうか?
変更理由が多いクラス
問題なのは、3つのメソッドが、まったく違った理由で変更される可能性があることです。
- calculatePay 給与計算に関わるルールが変わったら、修正が必要
- reportHours レポートのフォーマットが変わったら、修正が必要
- save データベーススキーマが変わったら、修正が必要
これらの変更は、おそらく別々のタイミングで発生するでしょう。
3つも変更理由がある従業員クラスは、非常に不安定な存在となります。
従業員クラスを分割する
クラスを分割したほうがよさそうなのは分かりました。ではどう分割すればよいでしょう?
そう、変更される可能性ごとに分けるのです。
// 従業員クラス
public class Employee {
// 給与計算を行う
public Money calculatePay() {
...
}
}
// 従業員報告書クラス
public class EmployeeReporter {
// 作業時間報告書を出力する
public String reportHours(Employee e) {
...
}
}
// 従業員リポジトリクラス
public class EmployeeRepository {
// データベースに保管する
public void save(Employee e) {
...
}
}
これで各々のクラスで、変更理由がひとつになりました。
神クラス(God Class)に立ち向かえ!
これが「従業員管理システム」だとすれば、従業員クラスはまさに根幹となるクラスです。
転勤する、昇格する、データバックアップする、リストプリントする。。どんな処理でも従業員に関する処理です。ほうっておくとすべて従業員クラスのメソッドになります。
このような、あまりにも多くを知り、あまりにも多くを行うクラスを、神クラス(God Class)といいます。
神クラスは、保守、拡張、使用、テストなど様々な局面で困難をもたらします。責任が単一となるよう分割しましょう。
まとめ
とはいっても、こと「責任」の考えは人によって違います。話はそれほど簡単になりません。
ひとつ考えかたがあります。責任を考えるとき、逆に「このクラスは、なにに責任を持たないのか?」と考えるのです。
従業員クラスは報告書フォーマットに責任は持たないし、DBのスキーマも決めるべきでない。であるなら、その処理は従業員クラスに実装すべきではないのです。
考え方のひとつになるのでないでしょうか。
- Single Responsibility Principle 単一責任原則
- Open Closed Principle 開放閉鎖原則
- Liskov Substitution Principle リスコフ置換原則
- Interface Segregation Principle インターフェース分離原則
- Dependency Inversion Principle 依存性逆転原則