2013-04-11 61 views
19

今、私はAutofacのIOCコンテナを使用して依存性注入パターンを自分自身で教えようとしています。私は以下に示す非常に簡単な例を思いついた。この例は単純ですが、正しく動作させることはできません。DIとの1つのインターフェイスに対する複数の実装

二つのモンスター、IMonsterインタフェースを実装し、両方:

interface IMonster 
{ 
    void IntroduceYourself(); 
} 

class Vampire : IMonster 
{ 
    public delegate Vampire Factory(int age); 

    int mAge; 

    public Vampire(int age) 
    { 
    mAge = age; 
    } 

    public void IntroduceYourself() 
    { 
    Console.WriteLine("Hi, I'm a " + mAge + " years old vampire!"); 
    } 
} 

class Zombie : IMonster 
{ 
    public delegate Zombie Factory(string name); 

    string mName; 

    public Zombie(string name) 
    { 
    mName = name; 
    } 

    public void IntroduceYourself() 
    { 
    Console.WriteLine("Hi, I'm " + mName + " the zombie!"); 
    } 
} 

それから私の墓地があります:

interface ILocation 
{ 
    void PresentLocalCreeps(); 
} 

class Graveyard : ILocation 
{ 
    Func<int, IMonster> mVampireFactory; 
    Func<string, IMonster> mZombieFactory; 

    public Graveyard(Func<int, IMonster> vampireFactory, Func<string, IMonster> zombieFactory) 
    { 
    mVampireFactory = vampireFactory; 
    mZombieFactory = zombieFactory; 
    } 

    public void PresentLocalCreeps() 
    { 
    var vampire = mVampireFactory.Invoke(300); 
    vampire.IntroduceYourself(); 

    var zombie = mZombieFactory.Invoke("Rob"); 
    zombie.IntroduceYourself(); 
    } 
} 

そして最後に私のメイン:

ここ

は私のクラス/インタフェースは、

static void Main(string[] args) 
{ 
    // Setup Autofac 
    var builder = new ContainerBuilder(); 
    builder.RegisterType<Graveyard>().As<ILocation>(); 
    builder.RegisterType<Vampire>().As<IMonster>(); 
    builder.RegisterType<Zombie>().As<IMonster>(); 
    var container = builder.Build(); 

    // It's midnight! 
    var location = container.Resolve<ILocation>(); 
    location.PresentLocalCreeps(); 

    // Waiting for dawn to break... 
    Console.ReadLine(); 
    container.Dispose(); 
} 

そして、これは私の問題である:実行時に 、Autofacは、この行の例外をスロー:

var vampire = mVampireFactory.Invoke(300); 

mVampireFactoryが実際にゾンビをインスタンス化しようとしているようです。もちろん、これは動作しません。なぜなら、ゾンビのコンストラクタはintを取らないからです。

これを解決する簡単な方法はありますか? また、オートファックの動作が完全に間違っていましたか? この問題をどうやって解決しますか?

+1

あなたは[Named Services](https://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices)を探している可能性があります。 –

+0

autofacは、墓地のクラスに対する2つのコンストラクタ引数をどのように解決しますか? – MattDavey

+1

MattDaveyに正解があります。リゾルバが両方のコンストラクタのためにFunc とFunc の特定を見つけることができないように見えるので、nullに置き換えます。おそらくfuncの両方をリゾルバに登録すると問題が解決する可能性があります – Fendy

答えて

22

コントロールコンテナの反転は工場自体ではありません。あなたのケースは工場パターンに最適です。

は、あなたのモンスターを作成するために使用される新しい抽象工場を作成します。

public interface IMonsterFactory 
{ 
    Zombie CreateZombie(string name); 
    Vampire CreateVampire(int age); 
} 

をそしてAutofacでその実装を登録します。

最後に、あなたのクラスでの工場を使用します。

class Graveyard : ILocation 
{ 
    IMonsterFactory _monsterFactory; 

    public Graveyard(IMonsterFactory factory) 
    { 
    _monsterFactory = factory; 
    } 

    public void PresentLocalCreeps() 
    { 
    var vampire = _monsterFactory.CreateVampire(300); 
    vampire.IntroduceYourself(); 

    var zombie = _monsterFactory.CreateZombie("Rob"); 
    zombie.IntroduceYourself(); 
    } 
} 

したい場合は、もちろん、あまりにも特定のモンスターのファクトリを使用することができます。そうでなければ、インターフェイスを使用することで、コードがもっと読みやすくなります。

更新

しかし、どのように私は工場を実装するのでしょうか?一方で、工場はIOCコンテナを使用してモンスターを作成するべきではありません。なぜならそれは悪とみなされるからです(DIパターンをサービス・ロケーターのパターンに劣化させる)。

私はSLが反パターンであると聞いて疲れています。そうではありません。すべてのパターンと同様に、誤って使用すると不都合が生じます。それはすべてのパターンに適用されます。 http://blog.gauffin.org/2012/09/service-locator-is-not-an-anti-pattern/

しかし、この場合、工場で直接実装を作成できない理由はわかりません。それは工場のためのものです:

public class PreferZombiesMonsterFactory : IMonsterFactory 
{ 
    public Zombie CreateZombie(string name) 
    { 
     return new SuperAwesomeZombie(name); 
    } 

    public Vampire CreateVampire(int age) 
    { 
     return new BooringVampire(age); 
    } 
} 

これ以上複雑ではありません。

一方、IOCコンテナをバイパスして工場とモンスターを密接に結びつけるため、工場はモンスター自体を作成すべきではありません。または、間違ったトラックに再びいますか? ;-)

工場がモンスターの実装に密接に結合していることは問題ではありません。これは、工場の目的です:オブジェクトの作成を抽象化することで、コード内にコンクリートを認識することはありません。

SuperDeluxeMonsterFactory,MonstersForCheapNonPayingUsersFactoryなどを作成することができます。アプリケーション内の他のコードはすべて、異なるモンスターを使用していることを認識していません。

コンクリートを交換する必要があるたびに、工場を切り替えるか、既存の工場を変更するだけです。あなたのモンスターの実装がLiskovs Substitution Principleに違反しない限り、他のコードは影響を受けません。

IoCコンテナ

対工場それでは工場とIoCコンテナの違いは何ですか? IoCは、クラスの依存関係を解決し、ライフタイムを維持するのに最適です(例えば、HTTPリクエストが終了したときにコンテナは自動的にすべてのディスポーザブルを処分できます)。

一方、工場はあなたのためのオブジェクトを作成することに優れています。それは何もしません。

概要

だから、どこかにあなたのコード内で使用すると、一般的に工場を使用する必要があり、実装の特定のタイプを取得する必要があります。ファクトリ自体は内部的に(依存関係を解決するために)サービスロケータとしてIoCを使用できます。これは、アプリケーション内の他の要素に影響を与えない、実装の詳細なので、これは問題ありません。

サービスを解決したい場合(依存関係の注入によって)、IoCコンテナを使用してください(どのインプリメンテーションを取得するか、以前に作成したインスタンスを取得するかどうかは関係ありません)。

+0

しかし、私はどのように工場を実装しますか?一方で、工場はIOCコンテナを使用してモンスターを作成するべきではありません。なぜならそれは悪とみなされるからです(DIパターンをサービス・ロケーターのパターンに劣化させる)。一方、工場はIOC容器を迂回し、工場とモンスターを密接に結びつけるため、モンスター自体を作成すべきではありません。または、間違ったトラックに再びいますか? ;-) – Boris

+0

更新を読む。 – jgauffin

+0

小さなLSPの説明:http://blog.gauffin.org/2011/05/liskovs-substitution-principle/ – jgauffin

関連する問題