📖

SOLID原則 ◆単一責任の原則◆

2022/01/09に公開

単一責任の原則とは

SOLID原則の、Single Reaponsibility Principleのことで、「モジュールはたったひとつのアクターに対して責務を負うべきである」という原則です。
「モジュール」は、ここではいくつかの関数やデータをまとめた凝集性のあるのものと捉えてください。
「アクター」はシステムを利用するユーザーやステークホルダーを指します。

なぜ、モジュールはたったひとつのアクターに対して責務を負うべきなのでしょうか?
それは、1つのモジュールが複数のアクターに対して責務を持っている状態だと、1つのアクターに対するモジュールの役割を変更したい場合に、別のアクターへの影響を気にしなければならないし、それ自体がBugの原因になってしまうからだと私は解釈しています。

文章だけでは理解が難しいので、Badコードと、Goodコードを見比べながら違い・メリットを理解したいと思います。

👎Badコード

例えばEmployeeクラスがあり、そこにcalculatePay()とreportHours()というメソッドが定義されているとする。
calculatePay()メソッドのアクターは経理部になり、reportHours()メソッドは人事部になる。
そして、それぞれのメソッドから共通アルゴリズム(regularHours())を使って、所定労働時間を算出しているとする。

そのような状態で、経理部が所定労働時間の算出方法を変えたい場合があったらどうなるか?
regularHours()に手を加えることになるが、この時点で人事部のreportHours()にも影響が出ることになってしまう。

「ちゃんとテストすれば大丈夫では?」「影響範囲をきちんと調査すれば大丈夫では?」と思いたくなりますが、「もしかしたらreportHours()への影響を見落として、Bugを出してしまうかもしれない」というリスクは生まれてしまうので、テストや調査をしたからといって、確実に0にできる保証もないですし、原則を守ることで、リスクをあらかじめ減らせるのであれば、それに越したことはないのかなと思います。

class Employee {
  // 給与計算をするメソッド
  calculatePay(){
    // 所定労働時間を算出
    regularHours();
  }

  // 勤務時間を集計するメソッド
  reportHours() {
    // 所定労働時間を算出
    regularHours();
  }
}

👍Goodコード

では、「原則に沿っていないコード」で述べたようなリスクを回避するために、原則に従ったコードはどのようなものか見ていきましょう。
今回は、デザインパターンの1つであるFacadeを使ってみます。
※Facadeについては別記事を書こうと思います。

Employeeクラスでは実行したいメソッドを持つクラスのインスタンスを生成して、
実際の処理はPayCalculatorクラスとHourReporterクラスに委譲しています。
Employeeクラスに共有データはまとめつつ、アクター別に関数を切り出し、Employeeクラスを窓口として使用することで、それぞれのクラスの責務は1つになります。

class Employee {
    id: number
    name: string 
    salary: number

    constructor(id: number, name: string, salary: number) {
        this.id = id
        this.name = name
        this.salary = salary
    }
}

// 経理部門が規定する
class PayCalculator {
    employeeData: Employee

    constructor(employee: Employee) {
        this.employeeData = employee
    }

    calculatePay() {}
}

// 人事部門が規定する
class HourReporter {
    employeeData: Employee

    constructor(employee: Employee) {
        this.employeeData = employee
    }

    reportHours() {}
}

参考資料

Clean Architecture

Discussion