はじめに
知り合いのビギナー開発者に依存関係逆転原則についてちょっとしたレクチャーをしたので、ここにおいておきます
Wikiにあるような定義の解説とは少々異なります
依存関係逆転原則の定義を見ても実際の開発と結びつかないそうなので、そのイメージをつかむためのレクチャーです
まず始めに簡単な問いを投げかけ、そこから例を交えて解説しました
当内容は多くのOOP言語で成り立つものだと思いますが、件のビギナーはJavaを用いているので、この記事もそれに準じたものであると断っておきます
問題
まず簡単な問いをさせてください
とあるコンポーネントAとそのプラグインとなるコンポーネントBがある
プラグインを利用するためにAはBを呼び出す必要があるが、特定のプラグインに依存することは避けたいためインターフェースB'を用意することにした
ここで、B'はコンポーネントAに属するべきか、それともコンポーネントBに属するべきか答えよ
選択肢
- コンポーネントAに属するべき
- コンポーネントBに属するべき
正解
- コンポーネントAに属するべき
解説
(この手の“原則”を扱って正解だの間違いだのいうのは少々大げさというかおこがましいとも思いますが...)
2だと思ってしまうのは、規約などで機械的にインターフェースを作成していた方によく見られる解答かと思います
依存関係逆転原則について、というよりインターフェースの使い方についての理解が浅い方は、BのインターフェースなのにAに属するってなんやねん、という疑問を持ったかもしれません
実際Javaなら
class B implements B'
などと書くことになりますから、そのように思ってしまうのはよくわかります
しかし、依存関係を制御するための道具としてインターフェースを用いる場合、正解は1となります
それは実際のアプリケーションを例にとれば明らかです
例えば、VSCodeなどのエディタとそのプラグイン(シンタックスハイライト機能など)の関係を考えてみるとよいでしょう
もし2が正解だった場合、プラグインを利用するためのインターフェースが各プラグイン毎に存在する、という状態になります
VSCode開発者は様々な人が作成するプラグインのコードをすべて確認し、それぞれに適した呼び出し処理をVSCode本体に用意する必要がでてきます
これではインターフェイスの意味がないですね
1の場合、VSCode本体の開発者がインターフェースを用意して外部に公開し、各プラグイン開発者は公開されたインターフェイズに基づいてプラグインを作成する形になります
すなわち、プラグインがVSCode本体に依存することになります
VSCode側は、以下のような呼び出し処理を記述し、ユーザーが現在選択しているプラグインの実体をインジェクションしてあげることとなるでしょう
// DIコンテナなどでインジェクション
B' hoge
execute() {
hoge.doSomething();
}
VSCodeのようなアプリケーション本体は、拡張機能であるプラグインよりも安定性が求められます
しかし、プラグインの機能を利用するためには当然ですが、プラグインの呼び出し処理の記述が必要になり、プラグインに依存しなくてはなりません
処理を呼び出すとは、その処理に依存するということです
例に挙げたエディタとプラグインの関係のように、安定しているべきものに不安定なものの呼び出し処理を記述しなくてはならないことはよくあります
そのような状況を、依存関係を逆転させることでひっくり返すことを説くのが依存関係逆転原則です
依存関係の逆転する様
AがBに依存することを仮に以下のようにあらわします
A→B
ここにインターフェースB’を挟むと
A→B'←B
さらに、インターフェースがどこに属するかを踏まえると
(A→B')=A
となるので
A←B
となります
最初の依存関係と逆転していますね
これが依存関係の逆転する様です
不安定なBが安定したAに依存する、という望ましい状態となりました
ここまで述べた依存関係逆転原則の考え方は、Javaでいうjar/Rubyでいうgemのような大きな枠組みに適応できるものです
例えばレイヤードアーキテクチャのレイヤー間のやり取りの処理で、インターフェイスが用いられることが多い理由も見当がついたのではないでしょうか
ビギナーの場合、JavaでいうClassファイル間の関係で物事を捉えることが多い印象ですが、コンポーネント単位などのより大きな枠で物事を捉えてみると、依存関係逆転原則についての理解が進むかと思います