【CleanArchitecture】読書メモ3
SOLID原則
SOLID原則は、関数やデータ構造をどのようにクラスに組み込むのか、そしてクラスの相互接続をどのようにするのかといったことを教えてくれる。
「クラス」という用語を使ったからといって、これらの原則がオブジェクト指向ソフトウェアにしか通用しないわけではない。ここでいうクラスとは、単にいくつかの機能やデータを取りまとめたものを指しているにすぎない。
solid原則の目的
以下のような性質を持つ中間レベル(モジュールレベルの開発に使われるもの)のソフトウェア構造を作ること
- 変更に強いこと
- 理解しやすいこと
- コンポーネントの基盤として、多くのソフトウェアシステムで利用できること
第7章 SRP: 単一責任の原則
- モジュールはたった一つのアクターに対して責務を負うべきである
- ユーザーやステークホルダー(アクタ-)こそがモジュール(ソースファイル)(いくつかの関数やデータをまとめた凝集性のあるもの)を変更する理由
症例1想定外の重複
このクラスは単一責任の原則(SRP)に違反している
- calculatePay()メソッドは経理部門が規定する。報告先はCFO。
- repostHours()メソッドは、人事部門が規定して使用する。報告先はCOO。
- save()メソッドはデータベース管理者が規定する。報告先はCTO。
- これらのメソッドを一つのEmployeeクラスに入れると、開発者はすべてのアクターを結合することになる。
- 例えばcalculatePay()メソッドとreporthours()メソッドの両方でえ所定労働時間を算出しているとして、計算アルゴリズムはどちらも同じ。
- この部分をregularHours()メソッドに切り出す
- ここでCFOチームでの所定労働時間の算出方法に手を加える必要が出てきたとする。COOのチームにはその必要がない。そのまま変更を加え、デプロイしてしまうとCOOチームの数字は間違った結果になる。
- よって、アクターの異なるコードは分割すべきである
解決策
どうするか
関数を別クラスに移動する
- データを関数から切り離す
- 例えば3つのクラスからEmployeeDataクラスを使うようにする。
- このクラスはシンプルなデータ構造を持つだけで、メソッドはひとつも含まれていない
弱点
この解決策には弱点があり、開発者が3つのクラスをインスタンス化して、追跡しなければいけないという点
ジレンマの解決策(Facadeパターン)
EmployeeFacadeに含まれるコードはごくわずかで、その責務は実行したいメソッドを持つクラスのインスタンスを生成して、処理を委譲するだけ。
重要なビジネスルールをデータの近くに置いておきたい場合は、元のEmployeeクラスに重要なメソッドだけを残し、重要ではないメソッドを呼び出すFacadeとして使えばいい
第8章 OCP: オープンクローズドの原則
ソフトウェアの構成要素は拡張に対しては開いていて、修正に対して閉じていなければならない
多くの開発者はOCPはクラスやモジュールを設計する際の指針となる原則だと考えているが、コンポーネントのレベルを考慮した時に、この原則はさらに重要なものとなる。
InteractorにはPresenterやViewを変更しても、なんの影響も及ばない。
PresenterはViewの変更の影響を受けないけど、Controllerの変更の影響は受ける。
ViewはControllerやPresenter、Interactor何を変更しても影響を受ける。
これがアーキテクチャレベルにおけるOCP。
いつどのような理由でどのように変更するかを考えて機能を分割する。そして、分割した機能をコンポーネントの階層構造にまとめる。上位レベルにあるコンポーネントは下位レベルのコンポーネントが変更されたとしても、変更する必要はない。
変更の影響を受けずにシステムを拡張しやすくすることがOCPの目的。
目的を達成するために、システムをコンポーネントに分割して、コンポーネントの依存関係を達成するために、システムをコンポーネントに分割して、コンポーネントの依存関係を階層構造にする。そして、上位レベルのコンポーネントが下位レベルのコンポーネントの変更の影響を受けないようにする。
第9章 LSP:リスコフの置換原則
第10章 ISP:インターフェイス分離の置換原則
第11章 DIP:依存関係逆転の置換原則
抽象インターフェイスの変更は、それに対応する具象実装の変更につながる。一方、具象実装を変更してもインターフェイスの変更が必要になることはあまりない。つまり、インターフェイスは実装よりも変化しにくい。
安定したソフトウェアアーキテクチャは、変化しやすい具象への依存を避け、安定した抽象インターフェイスに依存すべきである。これをコーディングレベルのプラクティスにまとめると、以下のようになる。
- 変化しやすい具象クラスを参照しない
- その代わりに抽象インターフェイスを参照すること。
- これをオブジェクトの生成時にも大きな制約となる。一般的にはAbstract Factoryパターンを使うしかない(AbstractFactory)
- 変化しやすい具象クラスを継承しない
- 静的型付け言語における継承は、ソースコードの中で最も今日よくかつ厳格なものであるため、注意しなければならない。
- 具象関数をオーバーライドしない。
- 具象関数はソースコードの依存を要求することが多い。関数をオーバーライドしても依存関係を排除することはできず、そのまま継承することになる。依存をうまく管理するには、元の関数を抽象関数にして、それに対する複数の実装を用意しなければいけない。
- 変化しやすい具象を名指しで参照しない。
- 依存関係逆転の原則を言い換えたもの
Discussion