2013-04-11 4 views
19

지금은 Autofac의 IOC 컨테이너를 사용하여 Dependency Injection 패턴을 가르치려고합니다. 나는 아래에 제시된 아주 간단한 예를 생각해 냈다. 예제는 간단하지만 제대로 작동하지 않습니다.DI가있는 하나의 인터페이스에 대한 여러 구현

두 괴물, 모두 IMonster 인터페이스 구현 : 마지막으로

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(); 
    } 
} 

그리고 내 주요 :

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!"); 
    } 
} 

그럼 내 묘지 거기를 여기

내 클래스/인터페이스입니다

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를 사용하지 않기 때문에 작동하지 않습니다.

이 문제를 해결하는 간단한 방법이 있습니까? 아니면 Autofac이 완전히 잘못 작동하는 방식을 얻었습니까? 이 문제를 어떻게 해결할 수 있습니까?

+1

[Named Services] (https://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices)를 찾고있을 수도 있습니다. –

+0

autofac은 묘화 클래스의 두 생성자 인수를 어떻게 해결합니까? – MattDavey

+1

MattDavey의 정답입니다. 해석자가 생성자에 대해 Func 및 Func 의 특정 항목을 찾을 수 없으므로 null로 바꿉니다. 어쩌면 두 함수를 모두 리졸버에 등록하면 문제를 해결할 수 있습니다. – 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 요청이 끝날 때 자동으로 모든 disposables를 처분 할 수 있습니다).

반면에 공장은 당신을 위해 객체를 만드는 데 뛰어납니다. 그것은 아무것도하지 않습니다.

요약

그래서 당신은 어딘가에 코드에서 당신은 일반적으로 공장을 사용해야 구현의 특정 유형을 얻을 필요합니다. 공장 자체는 IoC를 내부적으로 서비스 로케이터로 사용할 수 있습니다 (종속성을 해결하기 위해). 그것은 응용 프로그램의 다른 요소에 영향을주지 않는 공장의 구현 세부 사항이기 때문에 괜찮습니다.

서비스를 해결하려는 경우 (의존성 주입을 통해) IoC 컨테이너를 사용하고 (어떤 구현을 가져 오는지 또는 이전에 만든 인스턴스를 얻는 지 상관하지 않음).

+0

그러나 어떻게 팩토리를 구현합니까? 한편으로 공장은 몬스터를 만들기 위해 IOC 컨테이너를 사용하지 않아야합니다. 왜냐하면 그것은 악으로 간주되기 때문입니다 (DI 패턴을 서비스 탐지기의 안티 패턴으로 저하시킵니다). 반면에 공장은 IOC 용기를 우회하여 공장과 괴물을 단단히 연결하기 때문에 괴물 자체를 만들어서는 안됩니다. 아니면 내가 잘못된 길로 다시 간다? ;-) – Boris

+0

내 업데이트 읽기. – jgauffin

+0

작은 LSP 설명 : http://blog.gauffin.org/2011/05/liskovs-substitution-principle/ – jgauffin

관련 문제