[Clean Architecture 達人に学ぶソフトウェアの構造と設計] 第3部 設計の原則
この部では、クリーンなコードを書くためにSOLID原則をアーキテクチャ的にどのような意味を持つのかを中心に、関数やデータ構造をどのようにクラスに組み込むのか、そしてクラスの相互接続をどのようにするのか解説されている。 「クラス」と言われると、OOPにしか通用しないように聞こえるがそうではなく、ここで言うクラスとは単にいくつかの機能やデータをまとめたものを指している。
SOLIDと言うフレーズは次の各原則の頭文字をなぞったものである(各原則の詳細については後ほど)。
[S]ingle Responsibility Principle - 単一責任の原則
[O]pen Closed Principle - オープン・クローズドの原則
[L]iskov Substitution Principle - リスコフの置換原則
[I]nterface Segregation Principle - インターフェース分離の原則
[D]ependency Inversion Principle - 依存関係逆転の原則
また、SOLID原則の目的は次のように要約されている。
SOLID原則の目的は、次のような性質をもつモジュールレベルのソフトフェア構造を作ることである。 ・変更に強いこと ・理解しやすいこと ・コンポーネントの基盤として、多くのソフトフェアシステムで利用できること
(正直これだけだど雰囲気しか掴めない)
最後に、SOLID原則を知るに当たり頭の片隅に留めて置くことは、優れてたシステムを構築するには、SOLID原則に沿ったモジュールレベル(中間レベル)のコンポーネントだけではなく、コンポーネントの原則と上位レベルのアーキテクチャの原則の知識も必要となるということ。 (これら2つの原則については、SOLID原則が終わってから紹介されるらしい)
第7章 単一責任の原則(Single Responsibility Principle)
単一責任の原則は次のように要約されている。
モジュールはたったひとりのユーザーやステークホルダーに対して責務を負うべきである。
「たったひとり」というのは不適切らしい。というのも、複数のユーザーやステークホルダーがシステムを同じように変更したいと考えることもあるからだとか。 そこで、本書ではこれらの変更を望む人たちをひとまとめにしたグループとして扱い、このグループを「アクター」と呼んでいる。
これを踏まえて先ほどの要約を訂正すると次のようになる。
モジュールはたったひとつのアクターに対して責務を負うべきである。
個人的には前者の方がイメージを掴み安かった。(どうでも良いけど「イメージ」って他の言葉に置き換えると何がちょうど良いんだろ、雰囲気?大枠?こういう意味がふわふわした言葉って使う時にすごい便利に思えるんだけど、こういうふうに思うのは、何と無くの理解で済んで脳への負担が少ないからだったりすんのかな)
ちなみに「モジュール」については次のように説明されている。
モジュールはソースファイルのことである。だが、ソースファイル以外のところにコードを格納する言語や開発環境の場合、いくつかの関数やデータをまとめた凝集性のあるものだと考えて良い。
「ソースファイル以外のところにコードを格納する言語や開発環境」ってどんなものがあるんだろ?分かったら追記する。
この後に原則に反している例を上げ、より具体的に解説してくれている。
症例1: 想定外の重複
複数のアクターに対して責務を負うクラスに修正を加えることの危険性の高さを具体例を元に解説してあった。 終わりには、単一責任の原則を次のようにまとめている。
単一責任の原則は、アクターの異なるコードは分割するべきという原則だ。
症例2: マージ
これは題名の通りで、本来アクターが異なるコードは別々のクラスに実装すべきだが、それに反して1つのクラスに複数のアクターから責務を負うように実装しているとマージする必要が出てくるというか、コンフリクとしやすくなるよって話だった。
解決策
最後に解決策を述べている。 まず第1にアクターが異なる関数は別クラスを作成しそちらに移動する。 具体的な方法として次のように述べている。
一番分かりやすいのは、データを関数から切り離しすというものだろう。たとえば、3つのクラスからEmployeeDataクラスを使うようにする。このクラスは、シンプルなデータ構造を持つだけで、メソッドは1つも含まれない。3つのクラスはそれぞれ、特定の機能に必要なソースコードだけを保持している。また、他のクラスについて知ることは許可されていない。こうしておけば、想定外の重複は避けられる。
補足 3つのクラス・・・具体例ではEmployeeクラスに3つのメソッドを実装していて、それぞれが異なるアクターに対して責務を負うというものだった。ここでは、それら3つのアクターからの責務をばらばらにするために3つのクラスの定義を行っている。
この具体策では、データと関数を切り離すことでアクターの数分のクラスを再定義しそちらにメソッドを移動するということ推奨している。(これ知らず知らずの内にやっていたことなのかも。自分は開発中に、共通メソッドとデータからなるAクラスを作成し、それを継承したB、Cクラスを生やしてそれぞれでのみ必要なデータやメソッドを定義するみたいな事。この作業ってまさにアクターからの責務を分けている気がする)
筆者はこの解決策に対して、弱点があると言及したのちFacade(ファサード)パターンを持ちいる事でこれを回避できるとも述べている。
この解決策にも弱点がある。開発者が3つのクラスをインスタンス化して、追跡しなければいけないという点だ。このジレンマを解決するために一般的に使われるのが、Facadeパターンである。Facadeクラスに含まれるコードはごくわずかである。その責務は、実行したいメソッドを持つクラスのインスタンスを生成して、処理を委譲するだけだ。
補足 Facadeパターン(ファサード・パターン)・・・GoF(Gang of Four; 4人のギャングたち)によって定義された、コンピュータソフトウェアのデザインパターンの1つである。Facade(ファサード)とは「建物の正面」を意味する。異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする。Wikipediaより これだけだと良くわからなかったので、追加でこちらの記事たちもさらっと読んだ。RubyでFacadeパターン, サルでもわかる 逆引きデザインパターン 第3章 逆引きカタログ J2EE編 Facade(ファサード) 次から次に知らない単語が出てくる。。。今は引き出しを増やすの優先で、実際に手を動かして理解するのは仕事や個人開発の時って割り切って進める。
まとめ
単一責任の原則は関数やクラスに関する原則で、アクターの異なるコードは分割するべきという原則である。
補足 同じような原則が別のレベルであるコンポーネントレベルとアーキテクチャレベルでも存在し、それぞれ、閉鎖性共通の原則(CCP)、「アーキテクチャの境界」を作るための「変更の軸」と呼ばれている。