web-dev-qa-db-ja.com

コンストラクター注入とは何ですか?

(サービスロケーター)設計パターンに関する記事を読みながら、コンストラクターインジェクションと依存関係インジェクションという用語を見てきました。

コンストラクターインジェクションについてググると、結果が不明確になり、ここにチェックインするように求められました。

コンストラクター注入とは何ですか?これは特定のタイプの依存性注入ですか?標準的な例は大きな助けになるでしょう!

編集

1週間のギャップの後にこの質問を再訪すると、私がどれだけ失ったかを見ることができます...他の誰かがここにポップインした場合に備えて、私の質問を少し学習して更新します。自由にコメント/修正してください。 コンストラクタインジェクションとプロパティインジェクションは、依存性インジェクションの2つのタイプです。

51
TheSilverBullet

私は専門家ではありませんが、私は助けることができると思います。そして、はい、それは特定のタイプの依存性注入です。

免責事項:これのほとんどすべてが The Ninject Wiki から「盗まれた」

簡単な例を見て、依存関係注入のアイデアを見てみましょう。あなたが次の大ヒットゲームを書いているとしましょう。このゲームでは、高貴な戦士が偉大な栄光のために戦います。まず、戦士の武装に適した武器が必要です。

class Sword 
{
    public void Hit(string target)
    {
        Console.WriteLine("Chopped {0} clean in half", target);
    }
}

次に、戦士自身を表すクラスを作成しましょう。敵を攻撃するために、戦士はAttack()メソッドを必要とします。このメソッドが呼び出されると、Swordを使用して相手を攻撃します。

class Samurai
{
    readonly Sword sword;
    public Samurai() 
    {
        this.sword = new Sword();
    }

    public void Attack(string target)
    {
        this.sword.Hit(target);
    }
}

これで、侍を作成して戦闘を行うことができます!

class Program
{
    public static void Main() 
    {
        var warrior = new Samurai();
        warrior.Attack("the evildoers");
    }
}

ご想像のとおり、これはを出力します。邪魔者をコンソールにクリーンアップします。これはうまく機能しますが、サムライに別の武器を装備したい場合はどうでしょうか? SwordはSamuraiクラスのコンストラクター内に作成されるため、この変更を行うためにクラスの実装を変更する必要があります。

クラスが具体的な依存関係に依存している場合、それはそのクラスに密結合であると言われます 。この例では、SamuraiクラスはSwordクラスに密結合されています。クラスが密結合されている場合、実装を変更せずにクラスを交換することはできません。クラスの密結合を回避するために、インターフェースを使用して、一定レベルの間接参照を提供できます。ゲーム内の武器を表すインターフェースを作成してみましょう。

interface IWeapon
{
    void Hit(string target);
}

次に、Swordクラスはこのインターフェースを実装できます。

class Sword : IWeapon
{
    public void Hit(string target) 
    {
        Console.WriteLine("Chopped {0} clean in half", target);
    }
}

そして、侍クラスを変更できます。

class Samurai
{
    readonly IWeapon weapon;
    public Samurai() 
    {
        this.weapon = new Sword();
    }
    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }
}

今私たちの侍はさまざまな武器で武装することができます。ちょっと待って!剣はまだサムライのコンストラクター内で作成されます。武士に別の武器を与えるために、武士の実装を変更する必要があるため、武士は依然として剣と密接に結びついています。

幸い、簡単な解決策があります。 Samuraiのコンストラクター内からSwordを作成するのではなく、代わりにコンストラクターのパラメーターとして公開することができます。 コンストラクタインジェクションとも呼ばれます

class Samurai
{
    readonly IWeapon weapon;
    public Samurai(IWeapon weapon) 
    {
        this.weapon = weapon;
    }
    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }
}

Giorgioが指摘したように、プロパティインジェクションもあります。それは次のようなものです:

class Samurai
{
    IWeapon weapon;

    public Samurai() { }


    public void SetWeapon(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }

}

お役に立てれば。

99
Luiz Angelo

私はこれを非常に初歩的にするためにできるだけ努力します。このようにして、コンセプトに基づいて、独自の複雑なソリューションやアイデアを作成できます。

今、世界中に支部を持つジュビリーと呼ばれる教会があると想像してください。私たちの目的は、単純に各ブランチからアイテムを取得することです。 DIを使用したソリューションは次のとおりです。

1)インターフェースIJubileeを作成します。

public interface IJubilee
{
    string GetItem(string userInput);
}

2)IJubileeインターフェイスをコンストラクターとして受け取り、アイテムを返すクラスJubileeDIを作成します。

public class JubileeDI
{
    readonly IJubilee jubilee;

    public JubileeDI(IJubilee jubilee)
    {
        this.jubilee = jubilee;
    }

    public string GetItem(string userInput)
    {
        return this.jubilee.GetItem(userInput);
    }
}

3)次に、Jubileeの3つの分岐、つまりJubileeGT、JubileeHOG、JubileeCOVを作成します。これらはすべてIJubileeインターフェースを継承する必要があります。それを楽しむために、そのうちの1つに仮想メソッドを実装させます。

public class JubileeGT : IJubilee
{
    public virtual string GetItem(string userInput)
    {
        return string.Format("For JubileeGT, you entered {0}", userInput);
    }
}

public class JubileeHOG : IJubilee
{
    public string GetItem(string userInput)
    {
        return string.Format("For JubileeHOG, you entered {0}", userInput);
    }
}

public class JubileeCOV : IJubilee
{
    public string GetItem(string userInput)
    {
        return string.Format("For JubileCOV, you entered {0}", userInput);
    }
}

public class JubileeGTBranchA : JubileeGT
{
    public override string GetItem(string userInput)
    {
        return string.Format("For JubileeGT branch A, you entered {0}", userInput);
    }
}

4)以上です!次に、DIの動作を見てみましょう。

        JubileeDI jCOV = new JubileeDI(new JubileeCOV());
        JubileeDI jHOG = new JubileeDI(new JubileeHOG());
        JubileeDI jGT = new JubileeDI(new JubileeGT());
        JubileeDI jGTA = new JubileeDI(new JubileeGTBranchA());

        var item = jCOV.GetItem("Give me some money!");
        var item2 = jHOG.GetItem("Give me a check!");
        var item3 = jGT.GetItem("I need to be fed!!!");
        var item4 = jGTA.GetItem("Thank you!");

コンテナークラスのインスタンスごとに、必要なクラスの新しいインスタンスを渡します。これにより、疎結合が可能になります。

これが一言で概念を説明することを願っています。

3
David Grant

クラスAがあり、その各インスタンスには別のクラスBのインスタンスが必要であるとします。

_class A
{
   B b;
}
_

依存性注入とは、Bへの参照がAのインスタンスを管理するオブジェクトによって設定されることを意味します(AB 直接)。

コンストラクター注入とは、Bへの参照がパラメーターとしてAのコンストラクターに渡され、コンストラクターで設定されることを意味します。

_class A
{
    B b;

    A(B b)
    {
        this.b = b;
    }
}
_

別の方法として、セッターメソッド(またはプロパティ)を使用して、参照をBに設定します。この場合、aのインスタンスAを管理するオブジェクトは、最初にAのコンストラクターを呼び出し、後でセッターメソッドを呼び出してメンバー変数を設定する必要があります_A.b_ aが使用される前。後者の注入方法は、オブジェクトが互いに循環参照を含む場合に必要です。 bのインスタンスBのインスタンスaのインスタンスAaへの後方参照を含む場合.

**編集**

コメントに対処するための詳細を次に示します。

1。クラスAがBインスタンスを管理

_class A
{
    B b;

    A()
    {
        b = new B();
    }
 }
_

2。Bインスタンスはコンストラクターで設定されます

_class A
{
    B b;

    A(B b)
    {
        this.b = b;
    }
}

class M
{
    ...
    B b = new B();
    A a = new A(b);
}
_

。Bインスタンスはセッターメソッドを使用して設定されます

_class A
{
    B b;

    A()
    {
    }

    void setB(B b)
    {
        this.b = b;
    }
}

class M
{
    ...
    B b = new B();
    A a = new A();

    a.setB(b);
}
_
2
Giorgio