2012-04-23 2 views
21

나는 다음과 같은 코드가있는 경우 :IOC의 (Ninject에) 및 공장

public class RobotNavigationService : IRobotNavigationService { 
    public RobotNavigationService(IRobotFactory robotFactory) { 
    //... 
    } 
} 
public class RobotFactory : IRobotFactory { 
    public IRobot Create(string nameOfRobot) { 
    if (name == "Maximilian") { 
     return new KillerRobot(); 
    } else { 
     return new StandardRobot(); 
    } 
    } 
} 

내 질문은 여기 제어의 반전을 할 수있는 적절한 방법은 무엇입니까? Factory 클래스에 KillerRobot 및 StandardRobot 콘크리트를 추가하고 싶지 않습니다. 그리고 저는 IoC를 통해 그걸 가져오고 싶지 않습니다. <> 맞습니까? 그 진정한 IoC가 아닌 서비스 위치가 될 BC? 런타임에 콘크리트를 바꾸는 의 문제에 접근하는 더 좋은 방법이 있습니까?

+2

당신의 코드를 확인 할 수 있습니다 - 바로 첫 번째 줄은 법적 C#을하지 않습니다. –

+0

죄송합니다. 알림 주셔서 감사. 나는 게시하기 전에 그것을 고쳤다 고 생각했다. 지금 바로 잡아라. – BuddyJoe

답변

29

샘플의 경우 완벽하게 훌륭한 공장 구현이 있으며 아무 것도 변경하지 않습니다.

그러나 KillerRobot 및 StandardRobot 클래스에는 실제로 자체 종속성이 있다고 의심됩니다. IoC 컨테이너를 RobotFactory에 노출하고 싶지 않다는 데 동의합니다.

하나의 옵션은 Ninject에 공장 확장을 사용하는 것입니다

https://github.com/ninject/ninject.extensions.factory/wiki

그것은 당신에게 공장을 주입하는 방법은 두 가지 제공 - 인터페이스를, 그리고 아이 로봇 (또는 무엇이든)을 반환하는 Func을 주입하여. 인터페이스 기반의 공장 제작을위한

샘플 : 기반 FUNC에 대한 https://github.com/ninject/ninject.extensions.factory/wiki/Factory-interface

샘플 : https://github.com/ninject/ninject.extensions.factory/wiki/Func 당신이 원하는 경우

, 당신이 당신의 IOC의 초기화 코드에서 FUNC을 결합하여 할 수도 있습니다. 뭔가 같이 :

var factoryMethod = new Func<string, IRobot>(nameOfRobot => 
         { 
          if (nameOfRobot == "Maximilian") 
          { 
           return _ninjectKernel.Get<KillerRobot>(); 
          } 
          else 
          { 
           return _ninjectKernel.Get<StandardRobot>(); 
          } 

         }); 
_ninjectKernel.Bind<Func<string, IRobot>>().ToConstant(factoryMethod); 

탐색 서비스는 다음과 같을 수 있습니다 : - 아마하지 물론

public class RobotNavigationService 
    { 
     public RobotNavigationService(Func<string, IRobot> robotFactory) 
     { 
      var killer = robotFactory("Maximilian"); 
      var standard = robotFactory(""); 
     } 
    } 

,이 방법의 문제는 당신이 바로 IOC의 초기화 내부 팩토리 메소드를 작성하는 것입니다 최상의 트레이드 오프 ...

공장 확장은 몇 가지 규칙 기반 접근 방식을 제공함으로써이를 해결하려고 시도하므로 상황에 민감한 종속성을 추가하여 일반 DI 체인을 유지할 수 있습니다.

+0

확장 프로그램을 사용해 보니 - 나를 팔았습니다! +1 & 대답 – BuddyJoe

+0

@BuddyJoe는 위의 Factory 메서드를 사용하여 코드를 실행합니까? – ct5845

+0

나는 그랬다. 그리고 마지막 코드가 거의 이것에 가까웠다는 것을 기억하면. – BuddyJoe

3

나는 공장 클래스 내가 할 수있는 KillerRobot 및 StandardRobot 콘크리트를 추가 싶지 않아?

나는 아마도 그렇게하는 것이 좋습니다. 구체적인 객체를 인스턴스화하지 않는다면 공장의 목적은 무엇입니까? 나는 당신이 어디에서 왔는지 알 수 있다고 생각합니다. - IRobot이 계약을 기술하고 있다면, 주입 용기가 그것을 만들지 않아야합니까? 용기가 무엇을위한 것이 아닌가?

아마도. 그러나 객체를 반환하는 콘크리트 팩토리를 반환하는 것은 IoC 세계에서 꽤 표준적인 패턴으로 보입니다. 콘크리트 공장에서 실제 작업을하는 것이 원칙에 위배되는 것은 아니라고 생각합니다.당신이해야

+3

그의 샘플 코드로 설명하지는 않았지만 KillerRobot과 StandardRobot에는 실제로 종속물에 의해 해결되어야하는 추가 종속성이 있다는 점이 문제라고 생각합니다. –

+0

그리고 KillerRobot과 Standard Robot에 의존성이 있다면 어떻게 처리하겠습니까? – BuddyJoe

+1

공장 확장에서 인터페이스 또는 func 기반 접근 방식을 사용하는 경우 재귀 적 종속성 해결이 수행됩니다. –

2

방법 :

kernel.Bind<IRobot>().To<KillingRobot>("maximilliam"); 
kernel.Bind<IRobot>().To<StandardRobot>("standard"); 
kernel.Bind<IRobotFactory>().ToFactory(); 

public interface IRobotFactory 
{ 
    IRobot Create(string name); 
} 

그러나 IRobotFactory.Create를 호출 할 때 난 당신이 널 이름을 잃을 생각이 방법, 그래서 당신이 올바른 이름이 매개 변수를 통해 전송되는 확인해야합니다.

인터페이스 바인딩에서 ToFactory()을 사용하는 경우 IResolutionRoot를 수신하고 Get()을 호출하는 성 (또는 동적 프록시)을 사용하여 프록시를 만드는 것뿐입니다.

+0

네, 이렇게하고 싶습니다. +1 – ppumkin

0

저는 C# 클래스를 반환하여 코드 작업을 수행하는 막대한 switch 문을 정리할 방법을 찾고있었습니다 (여기에서 코드 냄새가납니다).

각 인터페이스를 ninject 모듈의 구체적인 구현 (사실상 긴 스위치 케이스를 모방하지만 diff 파일)에 명시 적으로 매핑하고 싶지 않았기 때문에 모든 인터페이스를 자동으로 바인딩하도록 모듈을 설정했습니다.

public class CarFactoryKernel : StandardKernel, ICarFactoryKernel{ 
    public static readonly ICarFactoryKernel _instance = new CarFactoryKernel(); 

    public static ICarFactoryKernel Instance { get => _instance; } 

    private CarFactoryKernel() 
    { 
     var carFactoryModeule = new List<INinjectModule> { new FactoryModule() }; 

     Load(carFactoryModeule); 
    } 

    public ICar GetCarFromFactory(string name) 
    { 
     var cars = this.GetAll<ICar>(); 
     foreach (var car in cars) 
     { 
      if (car.CarModel == name) 
      { 
       return car; 
      } 
     } 

     return null; 
    } 
} 

public interface ICarFactoryKernel : IKernel 
{ 
    ICar GetCarFromFactory(string name); 
} 

이 그런 다음 StandardKernel 구현이 얻을 수 있습니다

public class FactoryModule: NinjectModule 
{ 
    public override void Load() 
    { 
     Kernel.Bind(x => x.FromThisAssembly() 
          .IncludingNonPublicTypes() 
          .SelectAllClasses() 
          .InNamespaceOf<FactoryModule>() 
          .BindAllInterfaces() 
          .Configure(b => b.InSingletonScope())); 
    } 
} 

는 그런 다음 IKernal를 사용하여 단독 인스턴스를 통해 지정된 인터페이스와 그 구현을 얻을 것이다되는 StandardKernal을 구현 팩토리 클래스를 생성 여러분의 클래스를 꾸미는 인터페이스에서 여러분이 선택한 식별자로 인터페이스합니다.

예컨대 :

public interface ICar 
{ 
    string CarModel { get; } 
    string Drive { get; } 
    string Reverse { get; } 
} 

public class Lamborghini : ICar 
{ 
    private string _carmodel; 
    public string CarModel { get => _carmodel; } 
    public string Drive => "Drive the Lamborghini forward!"; 
    public string Reverse => "Drive the Lamborghini backward!"; 

    public Lamborghini() 
    { 
     _carmodel = "Lamborghini"; 
    } 
} 

사용법 :

 [Test] 
    public void TestDependencyInjection() 
    { 
     var ferrari = CarFactoryKernel.Instance.GetCarFromFactory("Ferrari"); 
     Assert.That(ferrari, Is.Not.Null); 
     Assert.That(ferrari, Is.Not.Null.And.InstanceOf(typeof(Ferrari))); 

     Assert.AreEqual("Drive the Ferrari forward!", ferrari.Drive); 
     Assert.AreEqual("Drive the Ferrari backward!", ferrari.Reverse); 

     var lambo = CarFactoryKernel.Instance.GetCarFromFactory("Lamborghini"); 
     Assert.That(lambo, Is.Not.Null); 
     Assert.That(lambo, Is.Not.Null.And.InstanceOf(typeof(Lamborghini))); 

     Assert.AreEqual("Drive the Lamborghini forward!", lambo.Drive); 
     Assert.AreEqual("Drive the Lamborghini backward!", lambo.Reverse); 
    }