SOLIDとパッケージ設計の原則とクリーンアーキテクチャとの関係性

SOLIDの原則とは?
SOLID原則とは、オブジェクト指向の設計で用いられる5つの原則のこと。 これらの原則は、ソフトウェアを変更に強く、テストしやすく、再利用しやすく、理解しやすくすることを目的とする。
- S: 単一責任の原則(SRP: Single Responsibility Principle)
- O: 開放閉鎖の原則(OCP: Open-Closed Principle)
- L: リスコフの置換原則(LSP: Liskov Substitution Principle)
- I: インターフェース分離の原則(ISP: Interface Segregation Principle)
- D: 依存性逆転の原則(DIP: Dependency Inversion Principle)

S: 単一責任の原則(SRP: Single Responsibility Principle)
単一責任の原則とは、モジュール、クラスまたは関数は、単一の機能について責任を持ち、その機能をカプセル化するべきであるという原則。
この原則を遵守することで、ソフトウェアの変更やテストが容易になり、コードの可読性や再利用性が向上する
単一責任の原則から外れてアンチパターンとして認識されているものとして以下のケースがある
- 多目的クラス
一つのクラスが複数の機能や関心事を持ってしまうこと。
例えば、ユーザー情報を管理するクラスが、データベースへのアクセスやバリデーションやビジネスロジックなどを担当してしまったり。
このようなクラスは、変更に弱く、テストしにくく、理解しにくくなる - 神クラス
多目的クラスの極端な例で、システムのほとんどの処理を担当する巨大なクラス。
このようなクラスは、変更やテストが非常に困難であり、メンテナンス性が低下する - 責任の不明確さ
クラスや関数の名前やコメントが、その実際の機能や責任と一致しない。
このような場合は、コードの可読性や信頼性が低下し、バグの発生や修正に時間がかかる

O: 開放閉鎖の原則(OCP: Open-Closed Principle)
開放閉鎖の原則とは、ソフトウェア要素(クラス、モジュール、関数など)は、拡張に対しては開いており、修正に対しては閉じているべきであるという原則。
この原則を遵守することで、ソースコードの修正をせずとも、各要素の振る舞いを拡張することが可能になり、保守性や再利用性が向上する
開放閉鎖の原則から外れてアンチパターンとして認識されているものとして以下のケースがある
- 条件分岐の乱用
一つのクラスや関数が複数の条件分岐を持ち、それぞれの分岐で異なる処理を行う。
例えば、動物の種類によって鳴き声を出力する関数が、if文やswitch文で動物ごとに処理を分けてしまう場合。
このような場合は、新しい動物を追加するたびにソースコードを修正しなければならず、開放閉鎖の原則に違反する - 責任過多
一つのクラスや関数が複数の責任や機能を持ってしまうこと。
例えば、カードゲームのクラスがカードの情報や山札や手札やルールなどを管理してしまう場合。
このような場合は、ソフトウェアの要件が変更されたときに、影響範囲が広くなり、テストやデバッグが困難になる
拡張に対しては開いており、修正に対しては閉じている状態とは?
ソフトウェア要素が新しい機能や要件に対応できるように柔軟でありながら、既存のコードを変更する必要がないように安定している状態。
例えば、動物の鳴き声を出力する関数を拡張するようなケースを考えると。。
拡張に対して開いており、修正に対して閉じている状態では新しい動物を追加するときに、関数の中身を変更するのではなく、動物クラスの継承やポリモーフィズムを利用して、関数の引数として渡すだけで対応できるようにする。
こうすることで、ソフトウェア要素が変更に強く、拡張に柔軟な状態を目指すのが開放閉鎖の原則。

L: リスコフの置換原則(LSP: Liskov Substitution Principle)
リスコフの置換原則とは、派生クラスは、その基底クラスと置換可能であるべきであるという原則。
この原則を遵守することで、型の互換性や拡張性が保たれ、コードの再利用性や可読性が向上する
リスコフの置換原則から外れてアンチパターンとして認識されているものとして以下のケースがある
- 事前条件の強化
派生クラスのメソッドが、基底クラスのメソッドよりも厳しい事前条件(引数や状態に関する制約)を持ってしまうこと。
例えば、正方形クラスが長方形クラスを継承しており、正方形クラスの幅と高さを変更するメソッドが、引数に同じ値しか受け付けない場合。
このような場合は、正方形クラスのオブジェクトは長方形クラスのオブジェクトとして置換できなくなる - 事後条件の弱化
派生クラスのメソッドが、基底クラスのメソッドよりも弱い事後条件(戻り値や副作用に関する制約)を持ってしまうこと。
例えば、鳥類クラスが動物クラスを継承しており、鳥類クラスの移動するメソッドが、飛ぶことしかできない場合。
このような場合は、鳥類クラスのオブジェクトは動物クラスのオブジェクトとして置換できない。

I: インターフェース分離の原則(ISP: Interface Segregation Principle)
インターフェース分離の原則とは、インターフェースを複雑にしてはいけないので、分離できるものは分離しましょうという原則。
この原則を遵守することで、クライアントに不要なメソッドへの依存を強制しないことができ、コードの可読性や保守性が向上する
インターフェース分離の原則から外れてアンチパターンとして認識されているものとして以下のケースがある
- 肥大化したインターフェース
ある1つのインターフェースに何もかもぶち込んでしまうこと。
例えば、ECサイトに出展される商品についてのインターフェースを作るとします。そのインターフェースは、商品情報を取得するメソッドがあるとします。ところが、ECサイトの利用者と出展者と、システム管理者では、取得したい情報に違いが生じる。
このような場合は、インターフェースを利用者用、出展者用、管理者用に分離するべき - 不完全なインターフェース
クライアントが必要とするメソッドがインターフェースに定義されていないこと。
例えば、動物クラスが食べるメソッドを持つインターフェースを実装しており、犬クラスが動物クラスを継承しています。しかし、犬クラスは鳴くメソッドも必要とする。
このような場合は、鳴くメソッドを持つ別のインターフェースを作り、犬クラスはそのインターフェースも実装するべき

D: 依存性逆転の原則(DIP: Dependency Inversion Principle)
依存性逆転の原則とは、あるモジュールが別のモジュールを利用するとき、モジュールはお互いに直接依存すべきではなく、どちらのモジュールも、共有された抽象(インターフェイスや抽象クラスなど)に依存すべきであるという原則。
この原則を遵守することで、プログラムの重要な部分が、重要でない部分に依存しないように設計でき、コードの柔軟性や再利用性が向上する
依存性逆転の原則から外れてアンチパターンとして認識されているものとして以下のケースがある
- 抽象が具象に依存する
抽象クラスや抽象メソッドが具象クラスや具象メソッドに依存してしまうこと。
例えば、動物クラスが食べるメソッドを持つインターフェイスを実装しており、犬クラスが動物クラスを継承sする。
しかし、動物クラスの食べるメソッドが犬クラスの食べるメソッドを呼び出してしまうと、動物クラスは犬クラスに依存してします。
このような場合は、動物クラスの食べるメソッドは抽象的な処理を記述し、犬クラスはその処理をオーバーライドするようにすべき

クリーンアーキテクチャとは?
ボブおじさんによって提唱されたアーキテクチャパターンの一つ
クリーンアーキテクチャでは、ソフトウェアを内側と外側に分けて、内側にはビジネスロジックやドメインモデルを、外側にはインフラストラクチャやプレゼンテーション層を配置する
そして、内側から外側への依存性を排除することで、変更に強くテストしやすいソフトウェアを実現させる
クリーンアーキテクチャとSOLID原則との関連
Clean Architecture 第Ⅲ部 設計の原則にSOLID原則が触れられている
Clean Architectureで扱われているコンポーネントレベルでSOLID原則が適用されている
具体的に適用されているSOLID原則は以下の様になる
- S: 単一責任の原則(SRP: Single Responsibility Principle)
クリーンアーキテクチャでは、各層やコンポーネントが単一責任を持つように設計される
レイヤーごとに責任を分けることで、変更が他のレイヤーに影響しないようにする - O: 開放閉鎖の原則(OCP: Open-Closed Principle)
クリーンアーキテクチャでは、内側の層が外側の層に対して開放されており、外側の層が内側の層に対して閉鎖される。
内側のレイヤーは外側のレイヤーに影響されないようにし、外側のレイヤーは内側のレイヤーを拡張することができる。 - L: リスコフの置換原則(LSP: Liskov Substitution Principle)
クリーンアーキテクチャでは、インターフェースを用いて抽象化された内側の層が、具体的な実装である外側の層に置き換えられるように設計される。
内側のレイヤーは外側のレイヤーの具体的な実装を知る必要がなくなる - I: インターフェース分離の原則(ISP: Interface Segregation Principle)
クリーンアーキテクチャでは、各層やコンポーネントが必要最低限のインターフェースを持つように設計される
内側のレイヤーが外側のレイヤーの具体的な実装を知る必要がなくなる - D: 依存性逆転の原則(DIP: Dependency Inversion Principle)
クリーンアーキテクチャでは、内側の層が外側の層に依存しないように設計される
モジュールは抽象に依存するようにし、これにより、内側のレイヤーは外側のレイヤーの変更に影響されなくなる

クリーンアーキテクチャでの内側と外側のレイヤーの分けている理由について
- 内側(高ベル)
ソフトウェアの設計における抽象度・重要度が高い
→ ソフトウェアの本質的な部分や目的を表すもの - 外側(低レベル)
ソフトウェアの設計における抽象度・重要度が低い
→ ソフトウェアの詳細な部分や手段を表すもの
具体的なコンポーネント
- 高レベル
ビジネスルールやドメインモデル - 低レベル
データベースやWeb API
重要なこと
高レベルなものは低レベルなものに依存しないようにする
目的
ソフトウェアの品質や柔軟性を向上させること

クリーンアーキテクチャとパッケージ設計の原則について
Clean Architectureの第Ⅳ部 コンポーネントの原則で、パッケージとコンポーネントの関係や、パッケージ設計の6つの原則について説明がある
また「Javaプロキシの例」で、パッケージ設計の原則を実際に適用した例が紹介されている
パッケージ設計の原則とは、再利用可能なコードをパッケージという単位にまとめる際の指針。
パッケージとは、関連性の高いクラスやモジュールなどをひとまとめにしたもので、フォルダやディレクトリなどで表現される。
パッケージは、再利用性や変更容易性を高めるために、依存関係や抽象度などに関する6つの原則に従って設計される
パッケージ設計の原則について
- コンポーネントの凝縮性
- 再利用・リリース等価の原則 (Reuse-Release Equivalence Principle: REP)
再利用の単位とリリースの単位は等価になるという原則。
パッケージ内のクラスはすべて再利用できるものか、すべて再利用できないもののどちらかにしなければならない。 - 全再利用の原則 (Common Reuse Principle: CRP)
パッケージに含まれるクラスは、すべて一緒に再利用されるという原則。パッケージ内のクラスは関連性が高く、凝集度が高いものでなければならない。 - 閉鎖性共通の原則 (Common Closure Principle: CCP)
パッケージに含まれるクラスは、みな同じ種類の変更に対して閉じているべきであるという原則。パッケージ内のクラスは同じ変更理由を持ち、結合度が低いものでなければならない。
- 再利用・リリース等価の原則 (Reuse-Release Equivalence Principle: REP)
- コンポーネントの結合
- 非循環依存関係の原則 (Acyclic Dependencies Principle: ADP)
パッケージ依存グラフに循環を持ち込んではならないという原則。パッケージ間の依存関係は一方向になり、循環依存がないものでなければならない。 - 安定依存の原則 (Stable Dependencies Principle: SDP)
パッケージの依存は常により安定したパッケージに向くべきであるという原則。変更の少ない安定したパッケージに対して依存するようにすることで、変更の影響を最小限に抑えることができる。 - 安定度・抽象度等価の原則 (Stable Abstractions Principle: SAP)
パッケージの抽象度と安定度は同程度でなければならないという原則。安定したパッケージは抽象的でなければならず、不安定なパッケージは具体的でなければならない。
- 非循環依存関係の原則 (Acyclic Dependencies Principle: ADP)
ここでいうコンポーネントはデプロイ単位を指している
マイクロサービスとの関連が強い。
コンポーネントとパッケージは、ソフトウェアを分割するための単位。それぞれ異なる目的や観点で分割する
コンポーネントとパッケージの違い
- コンポーネントは、再利用可能で交換可能なソフトウェア部品
コンポーネントは、特定の機能やサービスを提供し、他のコンポーネントとインターフェイスを通して連携します。コンポーネントは、デプロイの単位であり、コンテナやライブラリなどで表現される。 - パッケージは、関連性の高いクラスやモジュールなどをひとまとめにしたもの
パッケージは、再利用性や変更容易性を高めるために、依存関係や抽象度などに関する原則に従って設計される。
パッケージは、フォルダやディレクトリなどで表現される。 - コンポーネントとパッケージは、粒度が異なる
コンポーネントは、パッケージよりも小さく粒度が細かい。
コンポーネントは、パッケージ内に含まれることもあれば、パッケージ自体がコンポーネントとして扱われることもある。 - コンポーネントとパッケージは、分類の基準が異なる
コンポーネントは、ビジネスルールやドメインオブジェクトなどの概念に基づいて分類されることが多く、パッケージ内のクラスは関連性が高く、凝集度が高いものでなければいけない。
パッケージは、ビジネスケイパビリティやユースケースなどの概念に基づいて分類されることが多く、各サービスは疎結合であり、結合度が低いものでなければいけない
クリーンアーキテクチャで適用されるパッケージ設計の原則
クリーンアーキテクチャでは、下記のようなパッケージ設計の原則が適用されており、それぞれの層が再利用可能で変更しやすいものにしている
- クリーンアーキテクチャでは、各層がパッケージとして表現される。各層は、それぞれ独立した責務や機能を持ち、再利用・リリース等価の原則や全再利用の原則や閉鎖性共通の原則に従っている。
- クリーンアーキテクチャでは、内側から外側への一方向の依存関係がある。これは、非循環依存関係の原則や安定依存の原則に従っている。また、内側の層は外側の層はより安定するように設計しており、安定度・抽象度等価の原則に従っています。
- クリーンアーキテクチャでは、各層はインターフェイスを通してやり取りする。これは、依存関係逆転の原則を適用しており、具象ではなく抽象に依存することで、ソフトウェアは変更や拡張に対応しやすくしている

再利用・リリース等価の原則 (Reuse-Release Equivalence Principle: REP)
再利用・リリース等価の原則とは、再利用の単位とリリースの単位は等価になるという原則。
つまり、パッケージ内のクラスはすべて再利用できるものか、すべて再利用できないもののどちらかにしなければならない
この原則に違反した場合のデメリットは、以下のようなものがある
- 再利用する際に必要以上に多くのクラスをロードしなければならない。例えば、パッケージAには再利用できるクラスXと再利用できないクラスYが含まれているとする。この場合、クラスXを再利用するためにはパッケージA全体をロードする必要があるが、クラスYは不要で無駄なメモリや時間を消費することになる
- リリースする際に不必要な変更を伝播させなければならない。例えば、パッケージBには再利用できるクラスZと再利用できないクラスWが含まれているとする。この場合、クラスWに変更があったとしても、パッケージB全体をリリースする必要がある。しかし、クラスZを再利用しているユーザーにとっては、クラスWの変更は関係ない。これは不必要なリリースやテストを行わせることになる
上記の様に再利用・リリース等価の原則に違反した場合は、再利用やリリースの効率や品質を低下させることになる
上述の様なケースに対して具体的な改善方法は、以下のようなものがある
- 再利用できるクラスと再利用できないクラスを別々のパッケージに分ける
例えば、パッケージAからクラスYを取り除き、別のパッケージCに移動させることで、パッケージAは再利用できるパッケージとして整理できる。
これにより、クラスXを再利用する際にはパッケージAだけをロードすればよくなる。 - 再利用される頻度や目的が異なるクラスを別々のパッケージに分ける
例えば、パッケージBからクラスWを取り除き、別のパッケージDに移動させることで、パッケージBは再利用される目的や頻度が同じパッケージとして整理できる。
これにより、クラスWに変更があっても、パッケージBをリリースする必要がなくなる。
再利用・リリース等価の原則を守ることで、再利用やリリースの効率や品質を向上させることができる。

全再利用の原則 (Common Reuse Principle: CRP)
全再利用の原則とは、パッケージに含まれるクラスは、すべて一緒に再利用されるという原則。
つまり、パッケージに含まれるいずれかのクラスを再利用するということは、そのほかのクラスもすべて再利用することを意味する。
この原則に違反した場合のデメリットは、以下のようなものがある。
- 再利用する際に不必要なクラスをロードしなければならない
例えば、パッケージEには再利用できるクラスUと再利用できないクラスVが含まれているとする。
この場合、クラスUを再利用するためにはパッケージE全体をロードする必要があるが、クラスVは不要で無駄なメモリや時間を消費することになる。 - 再利用される頻度や目的が異なるクラスを同期させなければならない
例えば、パッケージFには頻繁に再利用されるクラスSとあまり再利用されないクラスTが含まれているとする。
この場合、クラスSに変更があったとしても、パッケージF全体をリリースする必要がある。
しかし、クラスTを再利用しているユーザーにとっては、クラスSの変更は関係がなく不必要なアップデートやテストを行わせることになる。
上記の様に全再利用の原則に違反した場合は、再利用やリリースの効率や品質を低下させることになる。
上述の様なケースに対して具体的な改善方法は、以下のようなものがある
- 再利用できるクラスと再利用できないクラスを別々のパッケージに分ける
例えば、パッケージEからクラスVを取り除き、別のパッケージGに移動させることで、パッケージEは再利用できるパッケージとして整理できる。
これにより、クラスUを再利用する際にはパッケージEだけをロードすればよくなる。 - 再利用される頻度や目的が同じクラスを同じパッケージに分ける
例えば、パッケージFからクラスTを取り除き、別のパッケージHに移動させることで、パッケージFは頻繁に再利用される目的や頻度が同じパッケージとして整理できる。
これにより、クラスSに変更があっても、パッケージFをリリースする必要がなくなる。
全再利用の原則を守ることで、再利用やリリースの効率や品質を向上させることができる。

閉鎖性共通の原則 (Common Closure Principle: CCP)
閉鎖性共通の原則とは、パッケージに含まれるクラスは、みな同じ種類の変更に対して閉じているべきであるという原則。
つまり、パッケージに含まれるクラスは同じ変更理由を持ち、結合度が低いものでなければならないということ。
この原則に違反した場合のデメリットは、以下のようなものがある。
- 変更する際に必要以上に多くのパッケージを変更しなければならなくなる
例えば、パッケージIにはビジネスロジックに関するクラスJとデータベースアクセスに関するクラスKが含まれているとする。
この場合、ビジネスロジックに変更があったとしても、パッケージI全体を変更する必要があるが、データベースアクセスに関するクラスKは不要で無駄な作業やエラーを増やすことになる。 - リリースする際に不必要な変更を伝播させなければならない
例えば、パッケージLにはUIに関するクラスMとドメインオブジェクトに関するクラスNが含まれているとする。
この場合、UIに変更があったとしても、パッケージL全体をリリースする必要がある。
しかし、ドメインオブジェクトに関するクラスNを再利用しているユーザーにとっては、UIの変更は関係なく不必要なリリースやテストを行わせることになる。
閉鎖性共通の原則に違反した場合は、変更やリリースの効率や品質を低下させることになる。
違反した場合の具体的な改善方法は、以下のようなものがある。
- 変更理由や種類が異なるクラスを別々のパッケージに分ける
例えば、パッケージIからクラスKを取り除き、別のパッケージOに移動させることで、パッケージIはビジネスロジックに関するパッケージとして整理できる。
これにより、ビジネスロジックに変更があった際にはパッケージIだけを変更すればよくなる。 - 変更頻度や影響範囲が同じクラスを同じパッケージに分ける
例えば、パッケージLからクラスNを取り除き、別のパッケージPに移動させることで、パッケージLはUIに関するパッケージとして整理できる。
これにより、UIに変更があった際にはパッケージLだけをリリースすればよくなる。
閉鎖性共通の原則を守ることで、変更やリリースの効率や品質を向上させることができる。

非循環依存関係の原則 (Acyclic Dependencies Principle: ADP)
非循環依存関係の原則とは、パッケージ依存グラフに循環を持ち込んではならないという原則。
つまり、パッケージ間の依存関係は一方向になり、循環依存がないものでなければならないということ。
この原則に違反した場合のデメリットは、以下のようなものがある。
- パッケージの安定度や抽象度を測ることができなくなる
例えば、パッケージQとパッケージRが互いに依存しているとする。
この場合、パッケージQの変更はパッケージRに影響を与え、パッケージRの変更はパッケージQに影響を与ええる。
これは、どちらのパッケージも安定していないことを意味する。
また、どちらのパッケージも抽象的でなければならないが、同時に具体的でなければならない。
これは、どちらのパッケージも抽象度が高いことを意味する。
このように、循環依存があると、パッケージの安定度や抽象度を測ることができなくなる。 - パッケージの再利用や交換が困難になる
例えば、パッケージSとパッケージTが互いに依存しているとする。
この場合、パッケージSを再利用するためにはパッケージTも必要で、パッケージTを再利用するためにはパッケージSも必要となる。
これは、どちらのパッケージも単独で再利用できないことを意味する。
また、パッケージSを別のパッケージUに交換するためにはパッケージTも変更しなければならず、パッケージTを別のパッケージVに交換するためにはパッケージSも変更しなければいけない。
これは、どちらのパッケージも単独で交換できないことを意味する。
非循環依存関係の原則に違反した場合は、パッケージの品質やメンテナンス性を低下させることになる。
違反した場合の具体的な改善方法は、以下のようなものがある。
- 依存関係を逆転させる
例えば、パッケージQとパッケージRが互いに依存しているとする。
この場合、どちらか一方のパッケージが他方のパッケージに対してインターフェイスを提供し、そのインターフェイスを実装することで依存関係を逆転させることができる。
これにより、一方向の依存関係になる。 - 依存関係を中間層に移動させる
例えば、パッケージSとパッケージTが互いに依存しているとする。
この場合、両方のパッケージが依存している共通の機能やデータを別のパッケージWに移動させることができる。
これにより、パッケージSとパッケージTはパッケージWに依存するようになる。
非循環依存関係の原則を守ることで、パッケージの品質やメンテナンス性を向上させることができる。

安定依存の原則 (Stable Dependencies Principle: SDP)
安定依存の原則とは、コンポーネントの依存関係は安定度の高い方向になるべきだという原則。
つまり、変更がしやすいコンポーネントは変更がしづらいコンポーネントに依存することができるが、その逆はできない。
この原則に違反した場合のデメリットは、以下のようなものがある
- 変更がしづらいコンポーネントが変更がしやすいコンポーネントに依存していると、変更がしやすいコンポーネントに変更があったときに、変更がしづらいコンポーネントも影響を受けて変更しなければいけない。これは変更の伝播や再利用の妨げになる。
- 変更がしづらいコンポーネントが変更がしやすいコンポーネントに依存していると、変更がしづらいコンポーネントの安定度が低下します。これは品質や信頼性の低下につながる。
具体的なケースとしては、以下のようなものが考えられる
- ビジネスロジックを担当するコンポーネントAがデータベースアクセスを担当するコンポーネントBに依存している場合、データベースの仕様や構造が変わったときに、コンポーネントAも変更しなければいけない。これはビジネスロジックとデータベースアクセスの関心を分離できていないことを意味する。
- UIを担当するコンポーネントCがドメインオブジェクトを担当するコンポーネントDに依存している場合、ドメインオブジェクトの仕様や構造が変わったときに、UIも変更しなければいけない。これはUIとドメインオブジェクトの関心を分離できていないことを意味する。
違反した場合の具体的な改善方法は、以下のようなものがある。
- 依存関係を逆転させる
例えば、コンポーネントAからコンポーネントBに依存している場合、コンポーネントBが提供する機能やデータを抽象化したインターフェイスを作り、コンポーネントAはそのインターフェイスに依存するようにする。
そして、コンポーネントBはそのインターフェイスを実装するようにする。
これにより、データベースの仕様や構造が変わっても、インターフェイスを通してやり取りすることで、コンポーネントAは影響を受けない。 - 依存関係を中間層に移動させる
例えば、コンポーネントCからコンポーネントDに依存している場合、両方のコンポーネントが共通で利用する機能やデータを別のコンポーネントEに移動させる。
そして、コンポーネントCとコンポーネントDはコンポーネントEに依存するようにする。
これにより、ドメインオブジェクトの仕様や構造が変わっても、コンポーネントEを通してやり取りすることで、UIは影響を受けない。
安定依存の原則を守ることで、コンポーネントの変更や再利用の効率や品質を向上させることができる。

安定度・抽象度等価の原則 (Stable Abstractions Principle: SAP)
安定度・抽象度等価の原則とは、コンポーネントの抽象度は、その安定度と同程度でなければならないという原則。
つまり、変更がしづらいコンポーネントは抽象的であるべきで、変更がしやすいコンポーネントは具体的であるべきであるということ。
この原則に違反した場合のデメリットは、以下のようなものがある。
- 変更がしづらいコンポーネントが具体的であると、そのコンポーネントに依存する他のコンポーネントも具体的な実装に依存することになる。これは、変更がしづらいコンポーネントに変更があったときに、依存するコンポーネントも影響を受けて変更しなければならなくなることを意味する。これは変更の伝播や再利用の妨げになる。
- 変更がしやすいコンポーネントが抽象的であると、そのコンポーネントの実装を隠蔽することになる。これは、変更がしやすいコンポーネントの内部構造や動作を理解することが困難になることを意味する。これはテストやデバッグの困難さにつながる。
具体的なケースとしては、以下のようなものが考えられる。
- ビジネスロジックを担当するコンポーネントXが変更がしづらいコンポーネントであるとする。この場合、コンポーネントXはビジネスロジックを抽象化したインターフェイスやクラスを提供するべき。しかし、もしコンポーネントXが具体的な実装やデータ構造を公開していると、その実装やデータ構造に依存する他のコンポーネントも変更しづらくなる。具体的な例として、ビジネスロジックに関係ないUIやデータベースアクセスのコンポーネントがビジネスロジックの詳細に依存してしまうなど。
- データベースアクセスを担当するコンポーネントYが変更がしやすいコンポーネントであるとする。この場合、コンポーネントYはデータベースアクセスの方法や仕様を明確に示すべき。しかし、もしコンポーネントYがデータベースアクセスを抽象化したインターフェイスやクラスだけを提供していると、そのインターフェイスやクラスの実装を隠蔽することになる。具体的な例として、データベースアクセスのパフォーマンスやエラー処理を確認することが困難になるなど。
原則に違反した場合の具体的な改善方法は、以下のようなものがある。
- 変更がしづらいコンポーネントは抽象的なインターフェイスやクラスを提供し、具体的な実装やデータ構造を隠蔽する
例えば、コンポーネントXはビジネスロジックを表すインターフェイスやクラスを提供し、その実装やデータ構造は別のコンポーネントに移動させることができる。
これにより、ビジネスロジックに依存する他のコンポーネントは抽象的なインターフェイスやクラスに依存するようになる。 - 変更がしやすいコンポーネントは具体的な実装やデータ構造を公開し、抽象的なインターフェイスやクラスを減らす
例えば、コンポーネントYはデータベースアクセスの方法や仕様を明確に示し、抽象化したインターフェイスやクラスは別のコンポーネントに移動させることができる。
これにより、データベースアクセスのパフォーマンスやエラー処理を確認することが容易になる。
安定度・抽象度等価の原則を守ることで、コンポーネントの変更や再利用の効率や品質を向上させることができる。

依存性逆転の原則を適用させるべきレイヤー
原則として内側から外側への一方向の依存関係になるようにする。
外側の層は内側の層に依存することができるが、その逆はさせない。
内側の層がより安定しており、変更されにくいことのメリットを活かす様にする。
しかし、実際には、内側の層が外側の層に対して何らかの機能やサービスを要求することがある。
例えば、ユースケース層がデータベースからデータを取得したり、プレゼンテーション層にデータを表示したりすることがある。
このような場合に、依存関係を逆転させるために、依存性逆転の原則を適用する。
具体的には内側の層が外側の層に対してインターフェイスを提供し、外側の層がそのインターフェイスを実装することで、具象ではなく抽象に依存するようにする
依存性逆転の原則を適用することで、以下のようなメリットを得られる
- 内側の層は外側の層から独立してテストや開発ができるようになる
- 外側の層は内側の層から隠蔽され、実装や技術を自由に変更できるようになる
- 内側の層と外側の層の間に明確な境界や契約ができるようになる