9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

依存関係逆転の原則(Dependency Inversion Principle) - 逆転ってなんやねん?

Posted at

いやいや、いきなり逆転って言われてもなんのこっちゃわからんわ

と思ったので。自分なりに調べたところを記録しておきます。

アーキテクチャについて調べているとよくお目にかかる、オブジェクト指向プログラミングの設計原則SOLID(Wikipedia)の五番目の子(D)だそうです。

逆転ってなんやねん?(翻訳:逆転とはどういう意味ですか?)

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

素直に実装したときの依存の方向と逆。

処理の流れは、ソースコードの依存性とは逆向きになることに注意しよう。ソースコードの依存性と処理の流れは逆向きになる。だからこそ「依存関係逆転の原則(DIP)」と名付けたのである。2

ソースコードの依存性と処理の流れが逆。

逆転の雰囲気については、なんとなくわかったような・・・?

どうやんねん?(翻訳:どのように実現すればよいのですか?具体的に教えてください。)

上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも、抽象に依存すべきである。
抽象は、実装の詳細に依存すべきではない。実装の詳細が、抽象に依存すべきである。
実践ドメイン駆動設計 p.118
3

実装の詳細が、抽象に依存すべき。

例として、MVPパターンでPresenterとViewを考えます。
素直に実装すると、Presenterが以下のようにViewに依存しています。どうやらこれが依存関係逆転の原則に違反しているようです。

Presenter
class Presenter {
    let view: View
    
    required init(view: View) {
        self.view = view
    }
    
    func updateText(_ text: String) {
        view.setText(text)
    }
}
View
class View: UIView {
    let label: UILabel
    ...
    func setText(_ text: String) {
        label.text = text
    }
}

これを抽象を使って、依存性を逆転させます。
つまり、Presenter側で定義した抽象にViewという具象を依存させます。

Presenter
// ViewProtocolという抽象を定義
protocol ViewProtocol: UIView {
    func setText(_ text: String)
}

class Presenter {
    // Presenterも同じく抽象に依存
    let view: ViewProtocol
    
    required init(view: ViewProtocol) {
        self.view = view
    }
    
    func updateText(_ text: String) {
        view.setText(text)
    }
}
View
// Viewを抽象に依存させる ← 逆転!
class View: UIView, ViewProtocol {
    let label: UILabel
    ...
    func setText(_ text: String) {
        label.text = text
    }
}

具体的なコードで見てみると、「ソースコードの依存性と処理の流れが逆」という意味がわかりますね。
Presenterは、実行時には具体的に実装されたViewのインスタンスを通じてメソッドを呼び出す(処理の流れ)けれど、ソースコード上では逆向きに依存してるよね、というお話です。

なにが嬉しいん?(翻訳:こうすることでどんな利益が得られるのですか?)

ソフトウェアは「ソフト」になるように考案された……振る舞いを簡単に変更する手段になることを目的としたもの……簡単に変更したくないときは、それを「ハード」ウェアと呼んだ。4

ソフトウェアは振る舞いを簡単に変更できなければいけない。

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

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

依存関係があると、変更のコストが大きくなる。

安定依存の原則 (SDP: Stable Dependencies Principle)

設計原則の一つに「安定依存の原則」があります。「依存の方向は、より安定した方向に向かわなければならない」というものです。実装を進めているとモジュール間の依存は必ず発生します。その方向を安定したモジュールに向かうようにすることで、ソフトウェア全体の修正頻度・修正範囲が低く小さく抑えられるというものです。安定依存の原則に従えば、ソフトウェアの安定性を高めることができます。5

依存する方向を安定したモジュールに向けるほど、変更に関わるコストが小さくなる。

抽象インターフェイスの変更は、それに対応する具象実装の変更につながる。一方、具象実装を変更してもインターフェイスの変更が必要になることはあまりない。つまり、インターフェイスは実装よりも変化しにくいということだ。
優れたソフトウェア設計者やアーキテクトは、インターフェイスの変動性をできるだけ抑えようとする。新しい機能を実装するときにも、できる限りインターフェイスの変更なしで済ませられるようにする。これは、ソフトウェア設計の基本中の基本だ。
安定したソフトウェアアーキテクチャは、変化しやすい具象への依存を避け、安定した抽象インターフェイスに依存すべきである。2

具象実装に比べて安定した抽象を依存先とすることで、安定したソフトウェアアーキテクチャに近く。

まとめると、簡単に変更できるようなソフトウェアを作るためには、依存先をできるだけ安定した(変更の少ない)対象にするほうが良いよ。
依存関係逆転の原則は、抽象を依存先にするという方法で、それに貢献しますよ、ということですね。

ほな、さいなら(翻訳:おわりに)

自分なりには上記のように理解したつもりですが、ほかの本や記事を読んでいるといろんな文脈で使われていて、もう少し違う解釈もあるのかなと思ったりします。
ご意見・ご感想お待ちしております!

  1. [よくわかるSOLID原則5: D(依存性逆転の原則)] (https://note.com/erukiti/n/n913e571e8207)より 2

  2. Clean Architecture 達人に学ぶソフトウェアの構造と設計(Robert C. Martin 著/角 征典、高木正弘 訳/アスキードワンゴ)第11章 DIP 2

  3. 1分でわかる依存関係逆転の原則(DIP)

  4. Clean Architecture 達人に学ぶソフトウェアの構造と設計(Robert C. Martin 著/角 征典、高木正弘 訳/アスキードワンゴ)第2章 2つの価値のお話

  5. 「依存性逆転の原則」の自分なりの解釈

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?