【Clean Architecture】第7章 単一責任の原則
「Clean Architecture」の学習記録。
「第7章 単一責任の原則」のまとめ。
原則の概要
この原則の名前を聞いたプログラマは、「どのモジュールもたったひとつのことだけを行うべき」と受け取ってしまう。
確かにそのような原則も存在する。最下位レベルの関数はたったひとつのことだけ行うべきである。
これは単一責任の原則とは別のものである。
単一責任の原則はかつて以下のように語られてきた。
モジュールを変更する理由はたった一つだけであるべきである。
「モジュールを変更する理由」とは、ユーザやステークホルダーを満足させるためである。
つまり以下のように言い換えられる。
モジュールはたったひとりのユーザやステークホルダーに対して責務を負うべきである。
複数のユーザやステークホルダーが同じように変更したい、と考えることもあるため、そのような変更を望む人たちをひとまとめにしたグループをアクターと呼ぶ。
つまり原則は以下のようになる。
モジュールを変更する理由はたった一つのアクターに対して責務を負うべきである。
症例1: 想定外の重複
この原則を理解するには、違反している例を見るのが一番良い。
給与システムにおけるEmployeeクラスを考える。
このクラスは単一責任の原則(SRP)に違反している。
3つのメソッドがそれぞれ別々の芥ーに対する責務を負っている。
- calculatePay() : 経理部門が規定する。CFOが利用する。
- reportHours() : 人事部門が規定する。COOが利用する。
- calculatePay() : データベース管理者が規定する。CTOが利用する。
これらのメソッドをひとつのEmployeeに入れるとすべてのアクターを結合することになる。
これが原因となり、CFOチームの何らかの操作が、COOチームの使うものに影響を及ぼしてしまうこともある。
例えばcalculatePay()とreportHours()の両方で所定労働時間を計算しているとする。
計算アルゴリズムはどちらも同じなためコードの重複を嫌い計算部分をregularHours()メソッドに切り出したとする。
ここでCFOチームで所定労働時間の算出方法に手を加える必要が出てきたとする。
CFOチームはregularHours()を修正してしまうと、reportHours()に影響が出てしまう。
問題が起こった原因は別々のアクターのコードをひとまとめにしてしまったことである。
単一責任の原則(SRP)はアクターの異なるコードは分割するべきという原則である。
解決策
この問題には様々な解決策があるが、いずれも関数を別のクラスに移動するというものである。
一番わかりやすい解決策は、データを関数から切り離すことである。
例えば3つのクラスからEmployeeDataクラスを使うようにする。
このクラスはシンプルなデータ構造を持つだけで、メソッドはひとつも含まれない。
3つのクラスはそれぞれ、特定の機能に必要なコードを保持している。
また他クラスについて知ることは許可されていない。
この解決策にも弱点がある。
それは開発者が3つのクラスをインスタンス化して、追跡しなければいけないという点である。
このジレンマを解決するために一般的に使われるのが、Facadeパターンである。
EmployeeFacadeに含まれるコードはわずかで、その責務は、実行したいメソッドを持つクラスのインスタンスを生成して、処理を委譲するだけである。
重要なビジネスルールはデータの近くに置いておきたい、と考える場合もある。
その場合は、元のEmployeeクラスに重要なメソッドだけを残し、重要ではないメソッドを呼び出すFacadeとして使えばよい。
メソッドが一つしかないクラスばかり作られるという理由で、この方法を気に入らない人もいる。
しかしそのようなことになるはずがない。
給与計算や帳票出力やデータの保存に必要なメソッドの数は多くなる。
また各クラスには、privateメソッドがいくつも含まれることになる。
まとめ
単一責任の原則(SRP)は関数やクラスに関する原則だが、同じような原則が別のレベルでも登場する。
コンポーネントレベルでは、この原則は「閉鎖性共通の原則(CCP)」と呼ばれる。
またアーキテクチャレベルでは、「アーキテクチャの境界」を作るための「変更の軸」と呼ばれる。