見出し画像

よくわかるSOLID原則5: D(依存性逆転の原則)

ソフトウェアエンジニアが知っているべきSOLID原則についての記事です。SOLID原則は、5つの原則の頭文字を並べた言葉で、S・O・L・I・Dそれぞれの原則について、5回に分けて説明する記事です。

1) Single Responsibility Principle:単一責任の原則
2) Open/closed principle:オープン/クロースドの原則
3) Liskov substitution principle:リスコフの置換原則
4) Interface segregation principle:インターフェース分離の原則
5) Dependency inversion principle:依存性逆転の原則

今回は5番目の依存性逆転の原則です。

なぜソフトウェアエンジニアがSOLID原則について知っていなければいけないかは最初の記事をご覧ください。

依存性逆転の原則

ものすごく簡単に結論からいうと、設計上望ましい依存の方向性と、素直に実装しようとしたときの方向性は矛盾しちゃうので、そこをテクニックでカバーして逆転させると、じつはスッキリと望ましい設計通りに実装できますよ!という人類の知恵です。

依存関係とは

依存関係というものがあります。プログラミングの世界では、たとえば、AというモジュールがBというモジュールを読み込む場合、AはBに依存しているといいます。

このとき、Bを変更するとAにも影響が生じます。このときの当然のことながらBだけの問題ではなく、Aについても影響範囲の調査や改修が必要になります。

特に破壊的変更を行うとその影響は甚大です。OCP(オープン・クローズドの原則)を思い出してください。

依存しているというのは、依存対象の知識を持っているということでもあります。AはBについての知識を持っているからこそ、Bのモジュールを読み込んで、その機能を利用できるのです。

望ましい依存の仕方とは

依存対象の知識ということでLSP(リスコフの置換原則)を思い出してみましょう。Aが依存しているというBは、果たしてどういうものなのでしょうか?

インターフェースか抽象クラスかあるいは他の何かはさておいて、LSPではBというモジュールが実際にどういう実装をしているかという知識を持つべきではありません。

このように依存対象について必要以上の知識を持たなければ、それは疎結合だといえます。逆に密結合とは互いに知識を多く共有しているということです。

また、SOLID原則では取り扱っていませんが、依存関係は有向非循環グラフ、まぁ簡単に言うと双方向依存禁止、循環禁止、というのが設計の重要な原則です。

望ましい依存の仕方は

・ 双方向依存しない
・ 依存関係が循環しない
・ 必要以上の情報を受け取らず疎結合にする

そしてもう1つあります。

抽象と詳細

抽象と詳細は、クリーンアーキテクチャ本で登場する呼び方です。抽象・具象という言い方もあります。抽象は共通点を束ねた物ではありますがそれだけではありません。詳細からエッセンスだけ抜き出したものともいえます。

リポジトリパターンのようなものは、抽象(インターフェース)に対して詳細(実装)は複数あり得るので、共通点を束ねる、抽象化したものですが、それだけではなく、詳細(実装)の中から外に漏れても仕方のない情報をそげ落として、エッセンスを抜き出したものも抽象(インターフェース)です。

SOLID原則を突き詰めると、抽象化されたものに依存すべきであり、詳細に依存してはいけないというのが導き出されます。

LSPでは、たとえばリポジトリパターンがあったとして、そのリポジトリのインターフェースの知識を持っている(つまり依存する)ことは許されますが、実装についての知識を持つこと(依存すること)は許されません。

・ 詳細に対して依存していはいけない

詳細に依存せずにアクセスする方法

ただ、詳細に対して依存してはいけないといっても、モジュール読み込みしたいのはインターフェースそのものではないはずです。実際の実装を読み込まなければ動作しないソフトウェアになってしまいます。

たとえばあるコードがリポジトリパターンを使ってデータを読み書きしたいとします。ところがリポジトリパターンを実装したコードの知識にアクセスせずに読み込むにはどうすればいいのでしょうか?

答えの1つは、DI(依存性の注入・Dependency Injection)です。OSやライブラリによって様々なDIの方法があるでしょう。これはインターフェースのみに依存しつつも、同時に詳細実装を読み込んでアクセス出来るようにしてくれます。

他にはファクトリーメソッドパターンを使うなどもあるでしょう。

どういう方法を使うにしても、詳細の実態となるオブジェクトを、依存元に渡す方法が何かしらあれば、それで実現が可能です。

まとめ

ここまで5つの記事で

1) Single Responsibility Principle:単一責任の原則
2) Open/closed principle:オープン/クロースドの原則
3) Liskov substitution principle:リスコフの置換原則
4) Interface segregation principle:インターフェース分離の原則
5) Dependency inversion principle:依存性逆転の原則

について取り扱ってきました。

依存される側は、露出している知識に対して責任を持つ必要があります。

SRP(単一責任の原則)では、複数の相手に責任を持つと大変なので単一の相手(アクター)に対する責任のみを持つ、つまり知識の露出を限定しようというものでした。

OCP(オープンクローズドの原則)では、既に知識が露出しているものに破壊的変更を加えると、広範囲な影響を及ぼすため、なるべく拡張する、つまり新たな知識を作り出すことで、機能を追加しましょうというものでした。

LSP(リスコフの置換原則)では、詳細についての知識を持たないと使えない抽象(インターフェース)には意味が無いのでそういうのはやめようね、露出する知識は最小限にしようというものでした。

ISP(インターフェース分離の原則)は、ある抽象が露出する知識が増えるとみんなが面倒くさいので、知識(というかインターフェース)を最小限にしましょう、そもそも役割の違う知識は別のものに分離しましょうというものでした。

最後にDIP(依存性逆転の原則)は、依存する、つまり知識を使う関係性を複雑にするとプロダクトが密結合になって破綻するので、依存関係はちゃんと考えた方が良く、それは大体実装しようとすると逆方向っぽく見えるので、いくつかあるデザインパターンなりライブラリなりを使って工夫しましょうというものでした。

SOLID原則は、知識・依存関係について考えるものであり、どうすれば大規模な密結合(スパゲティコード)という技術的負債を生み出さずに済むか?という人類の大切な知見です。

今回の5つの記事を元に、クリーンアーキテクチャについての同人誌を書こうという試行錯誤の中で生み出された文章で、まだ、推敲が足りてないなどがあると思います。

何かしら疑問、わかりにくい!みたいな感想、ここが間違っているなどの指摘などがあれば是非ともコメントをいただけると、とてもうれしいです。

よろしくお願いします。

告知: TypeScriptとクリーンアーキテクチャで、最高の開発者体験をしよう!

9/22(日曜)に池袋で開催される技術書典7で、東京ラビットハウスというサークルで、TypeScriptとクリーンアーキテクチャで、最高の開発者体験をしよう!という本を頒布予定です。

本書のターゲットは、バックエンドエンジニア・フロントエンドエンジニア両方です。複雑なコードとの戦いに疲れた人が、クリーンな設計で少しでも複雑さと戦わなくて済む為の本です。

良かったら、サークルチェックしてみてください。

この記事が気に入ったらサポートをしてみませんか?