kanachi-blog

notionでの公開記事をastro-notion-blogを使って公開するよ

【Clean Architecture 第Ⅲ部】設計の原則

目次

SOLID原則

SOLID原則の目的
  • 変更に強い
  • 理解しやすい
  • コンポーネントの基盤として、多くのソフトウェアシステムで利用できること
単一責任の法則(Single Responsibility Principle)

個々のモジュールを変更する理由がたった一つになるように設計すること

not モジュールはたったひとつのことだけを行うべき

but モジュールはたったひとつのアクターに対して責務を負うべきである。

💡
モジュール:関数やデータをまとめた凝集性のあるもの(クラスやモジュール)
アクター:システムに変更を望む人たちをひとまとめにしたグループ
違反例1:想定外の重複

給与システムにおけるEmployeeクラス、このクラスはcalculatePay()、reportHours()、save()メソッドが存在する。

  • caluculatePay()メソッドは、経理部門が規定する。報告先はCFO
  • reportHours()メソッドは、人事部門が規定する。報告先はCOO
  • save()メソッドは、データベース管理者が規定する。報告先はCTO

→3つのメソッドがそれぞれ別々のアクターに対する責務を負っている。

例えば、caluculatePay()とreportHours()の両方で所定労働時間を算出していて、計算アルゴリズムが同じだったので、regularHours()メソッドに切り出した。

ここでCFOチームの所定労働時間の算出方法に変更が生じた際に、regularHours()がreportHours()でも使われているメソッドだと知らずに改変して、それがデプロイされ損害が発生する。という事例が起こってしまう。

違反例2:マージ

また、CTOに関するシステム変更と、COOに関するシステム変更が生じる際に、双方の開発者が同じクラスに対してチェックアウトをして、変更するとマージが発生してしまうことがある。

解決策
  • データと関数を切り離す

上の弱点は開発者が3つのクラスをインスタンス化して、追跡しなければいけないという点

この弱点を補うために使われるのがFacadeパターン

  • データと関数を切り離して、Facadeパターンを利用する

参考:https://ja.wikipedia.org/wiki/Facade_パターン

オープン・クローズドの原則(Open Closed Principle)

ソフトウェアを変更しやすくするために既存のコードの変更よりも新しいコードの追加によってシステムの振る舞いを変更できるように設計すること

これを実現するためには

  1. 変更する理由が異なるものは単一責任の原則で適切に機能を分割する
  2. 分割した機能をコンポーネントの階層構造にまとめる

    ビジネスルールを含んでいるものは、アプリケーションの最上位レベルの方針を担っている。

    そのほかの階層構造は、自分のコンポーネントの周辺にある関心事(特定のコンポーネント)を上位にもつ。

  3. 上位レベルのコンポーネントが下位レベルのコンポーネントに依存しないようにそれらの依存関係を依存関係逆転の原則で適切にまとめる
方向の制御

依存関係を逆転させるために適切にインターフェースを用いる

情報隠蔽

インターフェースを用いて、自分が直接使ってないものを知りすぎないように、内部を隠蔽する

リスコフの置換原則(Liskov Substitution Principle)

交換可能なパーツを使ってソフトウェアシステムを構築するなら、個々のパーツが交換可能となるような契約に従わなければいけないということ。

i.e. ここで望まれるのは、次に述べるような置換可能な性質である:S型のオブジェクトo1の各々に、対応するT型のオブジェクトo2が1つ存在し、Tを使って定義されたプログラムPに対してo2の代わりにo1を使ってもPの振る舞いが変わらない場合、SはTの派生型であると言える

具体例

BillingアプリケーションはLicenceクラスを参照しており、Licenseの派生型である、PersonalLicenseとBusinessLicenseが存在するが、Billingは二つの派生型に関して依存してない。

違反例

様々なタクシー配車サービスを集約しているシステム開発をしている。

ユーザーがタクシーを決めたらシステムがRESTfulサービスを使ってタクシーを配車する。この時の呼び出しURIが以下のようにしている

purplecab.com/driver/Bob
	/pickupAddress/24 Maple St.
	/pickupTime/153
	/destination/ORD

このときいAcmeというタクシー会社が同じRESTインターフェースに準拠せずに、destinationというフィールド名をdestと省略すると、アーキテクチャはこの会社のためだけにif文を追加するという作業が発生してしまう。これはリスコフの置換原則を犯していると言える。

リスコフの置換原則は継承の使い方の指針というだけでなく、アーキテクチャのレベルにも適用するべきである。

インターフェース分離の原則(Interface Segregation Principle)

ソフトウェアを設計する際には、使っていないものへの依存を回避すべきだという原則

違反例

User1はop1、User2はop2、User3はop3、を使っている。

この時に、上記のような依存関係だと、User1は不必要なop2とop3にも意図せずに依存している

解決策

インターフェースに各操作を分離することで使っていないものへの依存を回避できる

依存関係逆転の原則(Dependency Inversion Principle)

上位レベルの方針のコードは、下位レベルの詳細の実装コードに依存するべきではなく、逆に詳細側が方針に依存するべきであるという原則

できる限り、ソースコードの依存関係が抽象を参照するように設計する。

  • 変化しやすい具象クラスを参照しない
  • 変化しやすい具象クラスを継承しない
  • 具象関数をオーバーライドしない
  • 変化しやすい具象を名指しで参照しない