web-dev-qa-db-ja.com

依存関係逆転の原理とハリウッドの例え

多くの場合、DIPはハリウッドの原則として知られています。「私たちに電話しないでください。電話します。」.

しかし、依存関係の逆転の有無にかかわらず、常に高レベルのモジュールが低レベルのモジュールを呼び出すとは限りません。

では、なぜDIPはハリウッドの原則と呼ばれるのでしょうか。

何か不足していますか?ありがとう!

5
q126y

OOコードを記述する一般的な方法は、次のようなコードを作成することです。

void SomeMethod()
{
    SomeClass x = new SomeClass(params...);
    ...
}

または次のようなコード:

void SomeMethod()
{
    SomeClass x = SomeStaticLocator.GetSomeClass();
    ...
}

どちらの場合も、コードはシステムのその他の部分を「要求」することで取得しています。

依存性注入/依存性反転では、反対(逆)のアプローチがとられます。

void SomeMethod(SomeClass x)
{
    ...
}

依存関係を求めるメソッドではなく、依存関係を通知します。これにより、システム内での結合が減少し、テストが大幅に簡素化されます。さらなる改善は、システムの部分をさらに分離する具体的なタイプではなく、インターフェースに設計することです。

void SomeMethod(ISomeClass x)
{
    ...
}

メソッド/クラスには依存関係が提供されているため、それ自体を要求する必要はないため、「伝える;聞かないでください」および「私たちに電話しないでください、私たちはあなたに電話します」という用語がよくまとめられます。 DIの動作。

8
David Arno

依存関係の逆転の原則は、「具体的な実装ではなく、抽象化へのコーディング」のファンシーな名前です。

手続き型プログラミングでは、Holywoodの原則は適用されません。アプリケーションのフローは、(ほとんどの場合)次のアクションが何であるかを決定するスイッチと条件を含む可能なループを上から下に向かって進みます。

これは、ソフトウェアプログラマによって事前に決定されているため、ユーザーはプロシージャの実行を制御できないことを意味します。

DIPは、呼び出し元(通常はアプリケーションのユーザー)が(たとえば)サービスを使用するまで、何が実行されるのか正確にはわからないことを意味します。

次の例について考えてみます。

_interface FancyStuff
{
    int GiveMeNumber();
    bool ProcessNumber(int number);
}
_

GiveMeNumber()メソッドを呼び出すことでintを操作して、booleanを返し、intを取得するProcessNumber()メソッドを使用できるようにするコントラクトを確立するインターフェイスがありますパラメータとして。

アプリのどこか、おそらくサービスレイヤーで、このインターフェイスに応じてクラスを持つことができます。

_class FancyStuffService
{
    private FancyStuff fancyStuff;

    public FancyStuffService(FancyStuff fancyStuff)
    {
        this.fancyStuff = fancyStuff;
    }

    public bool FancyOperation()
    {
        var number = this.fancyStuff.GiveMeNumber();
        ++number;
        return this.fancyStuff.ProcessNumber(number);
    }
}
_

一部のメソッドをどのように使用しているかに注意してください。ただし、実装はありません。クラスFancyStuffServiceには、メソッドGiveMeNumber()およびProcessNumber()の実装がどのように見えるかはわかりませんが、どの戻り値が期待され、どのような値が渡されるかがわかります。

当然、実装がなければ、インターフェースはほとんど役に立たないので、FancyStuffインターフェースの2つのバージョンを実装してロジックを提供します。

_class FancyStuffImplOne : FancyStuff
{
    int GiveMeNumber()
    {
        return 5;
    }

    bool ProcessNumber(int number)
    {
        return number != 0;
    }
}

class FancyStuffImplTwo : FancyStuff
{
    int GiveMeNumber()
    {
        return 42;
    }

    bool ProcessNumber(int number)
    {
        return number < 15;
    }
}
_

FancyStuffインターフェースの実装があり、インターフェースを使用するFancyStuffServiceがあり、それをすべて一緒に配線する必要があります。 しかしどうやって?

もちろん、あなたの工場で。これは、アプリケーションのすべての新しい演算子がある場所です。つまり、オブジェクトが構築される場所です。

_class FancyStuffServiceFactory
{
    private FancyStuff ObtainFancyStuffImplementation(int id)
    {
        switch (id)
        {
            case 1:
                return new FancyStuffImplOne();
            case 2:
                return new FancyStuffImplTwo();
            default:
                throw new RuntimException("Undefined fancy stuff ID.");
        }
    }

    public FancyStuffService BuildFancyStuffService(int fancyStuffId)
    {
        var fancyStuff = this.ObtainFancyStuffImplementation(fancyStuffId);
        return new FancyStuffService(fancyStuff);
    }
}
_

そして変数fancyStuffIdはまさにあなたが欲しいものです。整数は、実行時にユーザーからの入力を使用して、おそらくセレクトボックスまたはその他の方法で入力できます。

ユーザーが数値を挿入または選択した後にのみ、ファクトリが呼び出され、ユーザーの入力がファクトリに渡されます。これにより、ユーザーが使用するFancyStuffServiceが作成されます。これが完了すると、FancyStuffServiceクラスのFancyOperation()メソッドを呼び出すためにマップされたイベントが存在する可能性があります。

また、本当に必要になるまで、FancyStuffのどの実装が選択されるかがわからないことに注意してください。それがハリウッドの原理です。


DIPは依存関係の注入に直接結びついており、それなしでは機能しないことに注意してください。これは良いことです。依存性注入を考慮すると、グローバルな状態と静的メソッドが防止され、テスト可能なコードが作成されます。

3
Andy

A.高レベルモジュールは低レベルモジュールに依存しないでください。どちらも抽象化に依存する必要があります。

B.抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。

昔、遠く離れた銀河に2つのドロイドが存在していました。部屋を掃除するためにクリーナードロイド(モデルC-100)に定期的に要求を出すようにプログラムされたスーパーバイザードロイド(モデルS-100)です。クリーナードロイドは、ほうきをつかみ、そのホイールを使用して適切な場所に行き、汚れた部屋の掃除を開始するようにプログラムされました。

enter image description here

これは、mopとして知られている特定の状況用の新しいクリーニングデバイスが発明されるまで、すべてうまくいきました。

C-100はモップを使用するようにプログラムされていなかったため、ほうきとモップの両方を使用できるC-101と呼ばれる新しいクリーニングドロイドモデルを発明する必要がありました。

ただし、S-100はC-100以外と通信するようにプログラムされていないため、スーパーバイザーの新しいモデルであるS-101もC-101を考慮して作成する必要がありました。

enter image description here

...そしてこれが宇宙の秩序へと導く...

...dust mopとして神秘家に知られている新しい洗浄装置の発明まで。

この時点で、デザイナーはうんざりしていました。この新しいエイリアンクリーニングデバイスを処理するためにここで新しいC-102モデルを作成するには、同様に新しいS-102モデルが必要です。

この問題を解決するために、長老たちは召集し、これらのすべての洗浄装置を抽象的な洗浄インターフェースで標準化する必要があると決定しました。

enter image description here

この抽象的なクリーニングインターフェイスにより、新しいC-102は、この共通インターフェイスを介して、互換性のある任意のクリーニングデバイスと連携することができました。残念ながら、これはまだ新しいS-102をC-102とインターフェースするように設計する必要がありましたが、設計者は今後導入される新しい洗浄装置には新しいスーパーバイザモデルが必要ないことを確信しました。

enter image description here

...そして、秩序は銀河で再び復元されました...

...legsと呼ばれる新しい輸送装置が発明されて、ドロイドがより複雑な環境で機能するようになりました。

この時点で、SおよびCシリーズのドロイドの設計者は崖から飛び降りました。

長老たちは召集され、問題を解決するために新しい設計チームを招いた。新しい設計チームは、Sモデルへのさらなる変更を回避するために、Cクリーニングドロイドモデルにも、スーパーバイザーモデルと通信するための抽象的なインターフェイスが必要であることを認識しました。加えて、彼らは同様の扱いが抽象的な交通インターフェースにも必要であることに気づきました。

enter image description here

...そして清浄度は春のそよ風のように宇宙全体に広がっています(もちろん、この銀河には春の時間は存在しませんでしたが...).

...ホバーボードが発明されて、脚と車輪は時代遅れになりました。

これにより、新しいデザインチームは、儀式的なライトサーベル切腹を実行しました。その後、長老たちは別の設計チームを連れて、前の設計チームを置き換えました。

この時点で、設計チームCは、スーパーバイザードロイドを変更する必要はありませんでしたが、クリーニングドロイドが使用できるさまざまなタイプのすべてのクリーニングおよび輸送装置を認識するためのプログラミングがまだ必要であることに気付きました(上図の赤で表示) )。

彼らはまた、単にクリーンルームへの要求を定期的に発行するようにプログラムされたスーパーバイザードロイドは、実際にはクリーナードロイドよりもはるかに簡単に変更でき、クリーナードロイドよりも一般的な適用性がはるかに低いことを認識しました。

したがって、設計者はスーパーバイザードロイドからの互換性のあるクリーナーデバイスと互換性のあるトランスポートデバイスの使用要求をクリーナードロイドに受け入れさせることにしました。

また、青が好きであることを理解した後、回路図の抽象インターフェイスに青を使用することを決定しました。

enter image description here

...したがって、依存関係の逆転依存関係の注入の両方の基本的な考え方と原始的な形式が生まれました。将来的にさらに装飾されます。

この新しいデザインで奇妙なことが起こりました。掃除ロボットがこれらの低レベルの掃除および輸送装置を直接使用するようにプログラムされている代わりに、どの種類の掃除および輸送装置を使用するかが伝えられるようになりました。非常に限定されたA.I.を使用して、クリーニングドロイドの新しいプロトタイプモデルは、特定のデバイスを使用するようにプログラムされなくなったため、現在使用するクリーニングデバイスを設計者に尋ねようとしました。 「心配する必要はありません。モデルには必要に応じて何を使用するかが通知されますが、抽象的な要件に適合する互換性のあるクリーニングおよび輸送装置が提供されるので安心してください。」デザイナーは言った。

その後、ランダムな通行人がこの回路図を見て、遠心性および求心性のカップリングについて不明瞭なものを落書きし、不安定なメトリックを生成しながら、パッケージが簡単にできることについて謎めいたものをつぶやきました変更されます。

enter image description here

設計者がスーパーバイザードロイドの仕事が多すぎることに気づき、長老たちは設計者と彼らが働いていた工場が回路図に含まれるべきだと気づくまで、銀河は彼らの新しい柔軟な設計に喜びました。

終わり

2
user204677

制御の反転

@davidarnoで示されているように、下から上に構築は、最初に小さいビットをインスタンス化してから、それを、それが属するオブジェクトのコンストラクターに渡します。つまり、コンポジットの構成が逆になっています。

この Object Oriented Dice の例も参照してください。


IOCは依存性注入を有効にします

次に、コンストラクターを介して必要なオブジェクト(依存関係)を渡します。ボラ。依存性注入。パラメーターを渡すIS依存性注入。ただし、より大きなポイントは、さまざまなオブジェクトを注入できるようになったことです。基本型とインターフェイスを指定することにより、さらに柔軟性があります。


ハリウッド原則

Head First Design Patterns からの引用

Hollyworkの原則は、「依存関係の腐敗」を防ぐ方法を提供します。依存関係の腐敗は、高レベルのコンポーネントに依存する高レベルのコンポーネント、高レベルのコンポーネントに依存する高レベルのコンポーネント、および低レベルのコンポーネントに依存する横向きのコンポーネントなどがある場合に発生します。

Hollyworod原理では、低レベルのコンポーネントがシステムにフックすることを許可しますが、高レベルのコンポーネントは、いつ、どのように必要になるかを決定します 。言い換えると、高レベルのコンポーネントは低レベルのコンポーネントに「私たちと呼ばないで、私たちはあなたと呼ぶ」扱いを与えます。


Bob Martinによるクリーンアーキテクチャ

ハリウッド原則は記事でご覧いただけます

enter image description here

このアーキテクチャを機能させるオーバーライドルールは、依存関係ルールです。このルールは、ソースコードの依存関係は内部のみを指すことを示しています。

0
radarbob