SOLIDの観点から見たこの原則の私的見解
基本的にはSOLIDにおいて「バリエーションがあるコードを書く際に意識する原則の一つ」になります。
バリエーションを意識したコードを書く際に意識する際の原則として他には「オープンクローズドの原則」、「リスコフの置換原則」などがありますが、それらを別視点(モジュール依存関係的な視点)で言い換えた原則と言えるでしょう。(実装コード例はほぼ同じになる)概ね実装する際は、「依存関係逆転の原則」と「オープンクローズドの原則」、「リスコフの置換原則」はセットで意識する原則みたいなものとして捉えておくと良いでしょう。
依存関係逆転の原則とは?
上位レベルのモジュールは下位レベルのモジュールに依存すべきではない。どちらも抽象に依存すべきであるという原則。
上位モジュール
下位モジュールを呼ぶ。ユーザーが触れる部分。
- 画面など
下位モジュール
上位モジュールから呼ばれる。例えば、外部通信などを行う。(ファイル、DB、各種通信など)
上記のように抽象に依存させましょうという感じの実装です。
DI(依存性の注入)
この外部から上位モジュールに対して下位モジュールを注入することをDI(依存性の注入)とも言います。「依存関係逆転の原則」を実装するには「DI(依存性の注入)」も同時に意識することになりますので覚えておきましょう。
原則違反の例
- クライアントコードから直接「OracleDBからデータを取得する」というような具体的なコードを書いてしまうなど。
抽象は詳細に依存してはならない。詳細が抽象に依存すべきであるという原則。
例
- 抽象:「ボタンを押したら保存する」、詳細:「ボタンを押したらOracleDBに保存する」
なぜこの原則を守った方が良いのか?
下位モジュールがないと動かないため上位モジュールの再利用性がない。
下位モジュールに依存している時点で「下位モジュールがないと動かない」というプログラムになってしまっているということになるため。Oracleなど特定のアーキテクチャに紐づいてしまっている画面などは、アーキテクチャが変わった場合にそちらのコードにすぐに再利用できなくなってしまいます。
抽象(ユーザーがやりたいこと)は変わりづらく、詳細(テクニカル部分)は変わりやすいため。
抽象(ユーザーがやりたいこと)は変わりづらいです。例えば、ATMに顧客が振り込むというシステムがあったら「ATMの振り込みボタンを押す」という動作だったり、「ATMで振り込み操作が行われたら何かに保存をする」というフローはなかなか変わりづらいためです。
逆に、詳細コード(テクニカル部分)は変わりやすいです。例えば、今はオンプレミスのOracleを使っていても、今後クラウドのAuroraに移行したいといった場合に詳細に依存していると上位モジュールの書き換えが必要になりコードの書き換えが大変になってしまいます。
ダミーコードやテストコードを書く際にも便利になる。
また、ダミーデータや、テストコードを記述するときも便利です。(例えば、テスト時はAmazonS3ではなくminioに接続するようにするなどの場合にも対応できます。)
具体的には、getDataというメソッドがあった場合にOracleからデータを取ってくるような本実装クラスがあった場合に、テストコードではOracleに接続せずに、getDataというMockのメソッドなどを作ってあげて返してあげるようにしてそのMockのクラス自体を外側からテスト対象のクラスに対して与えてあげます。(依存性の注入)
Mockを毎回書くのは大変
テストコードを書くためにMockを毎回書くのは大変です。そのためC#などの言語ではMoqなどのライブラリが用意されていたりします。(返すデータを都度setupできるツール。まあ、データを用意する手間という点ではそこまで変わらないですが。わざわざMock用のダミークラスを作る必要はなくなります。)
実リソースを使った方が良い場合もある
もちろん、実リソースを使ってテストをするということはできなくはないです。(テストするたびにDBのデータを削除して、真っさらなデータを投入し直して再度テストするみたいなことはできなくはないので。)、もし、例えばOracleでストアドを使っていて外部インフラにロジックがある場合などはそのようにテストせざるおえないでしょう。最近はビジネスロジックはプログラム言語で補完した方が良い設計という風潮はありますが。
下位モジュールの実装を強制化できる
下位モジュールを抽象に依存させることで下位モジュールの実装を強制化することができます。例えば、saveというメソッドを抽象クラスに追加したら全ての下位モジュールでsaveというメソッドを追加しなければなりません。
そうすることで上位モジュールのコードに影響を与えません(少なくともコンパイルエラーにはなりません)
この記事へのコメントはありません。