2013-04-08 3 views
1

WPF 응용 프로그램에서 작업 중입니다.
나는 StructureMap을 사용하여 종속성을 주입합니다.
생성자에서 매개 변수를 제공하는 일부 서비스 계층 클래스가 있습니다.
생성자에 전달한 값에 따라 런타임이 변경됩니다.
프리젠 테이션 레이어의 클래스는 서비스를 사용하여 사용자에 대한 데이터를 제공합니다. 가치가 변할 때마다 저는 새로운 가치로 다시 봉사에 임했습니다. 그러나 프리젠 테이션 레이어의 활성 인스턴스는 이전 값을 반환합니다.
더 나은 이해를 위해 간단한 예제를 준비했습니다.기존 개체의 종속성 변경

// static class that keeps some value 
public class ValueKeeper 

{ 
    public static string Value { get; set; } 
} 

public interface IService 
{ 
    string Value { get; set; } 
} 
// Service layer class 
public class Service : IService 
{ 
    // default constructor 
    public Service(string value) 
    { 
     Value = value; 
    } 

    #region IService Members 

    public string Value { get; set; } 

    #endregion 
} 


public class Program 
{ 
    private readonly IService _service; 
    //injecting service class 
    public Program(IService service) 
    { 
     _service = service; 
    } 
    // structuremap configuration 
    private static void Config() 
    { 
     ObjectFactory.Initialize(x => x.Scan(scanner => 
               { 
                scanner.TheCallingAssembly(); 

                scanner.WithDefaultConventions(); 
                x.For<IService>().CacheBy(InstanceScope.Hybrid).Use(() => 
                              { 
                               var service = new Service("value1"); 
                               return service; 
                              }); 
               })); 
    } 
    // structuremap configuration after value changed. 
    private static void ReConfig() 
    { 
     ObjectFactory.Configure(x => x.Scan(scanner => 
               { 
                x.For<IService>().CacheBy(InstanceScope.Hybrid).Use(() => 
                              { 
                               var service =new Service(ValueKeeper.Value); 
                               return service; 
                              }); 
               })); 
    } 


    private string PresentationMethod() 
    { 
     return _service.Value; 
    } 

    private static void Main(string[] args) 
    { 
     Config(); // Firtst time injecting dependencies 
     var prog = ObjectFactory.GetInstance<Program>(); 
     Console.WriteLine(prog.PresentationMethod()); // returns "value1" 
     ValueKeeper.Value = "value 2"; //changing static property 
     ReConfig(); // reconfig service class with new property 
     Console.WriteLine(prog.PresentationMethod()); // it returns value1 but I expect value2 . 
     Console.ReadKey(); 
    } 
} 

실제 응용 프로그램에는 많은 프레젠테이션 및 서비스 클래스가 포함되어 있습니다.
어떻게 새 객체와 값으로 실제 서비스 인스턴스를 변경할 수 있습니까?


업데이트 : 나는 this 링크를 보았다. Setter Injection을 사용하면 기존 개체를 변경할 수 있습니다.
세터 주입은 내 솔루션입니까?

+0

이 주입 값을 인터페이스의 속성으로 만들 수 없으므로 매번 새로운 클래스 인스턴스를 주입 할 필요가 없습니까? 매번 생성자 매개 변수 만 변경되는 경우 종속성을 다시 주입하는 것은 끔찍한 연습처럼 보입니다. –

+0

@SimonWhitehead 나는 할 수 없다. 왜냐하면 내가 테스트 한 서비스 클래스는 실제로 .NET 라이브러리의 클래스이기 때문이다. (DbContext). 생성자에 연결 문자열을 전달해야합니다. – Shahin

+1

@shaahin IoC는 자신이 만든 인스턴스 (AFAIK 제외)를 추적하지 않습니다. 그래서, 짧은 대답은 컨테이너로 이것을 할 수있는 방법이 없다는 것입니다. 당신이 할 수있는 것은 당신이 제어하는 ​​메소드에 GetInstance() 메소드를 랩핑하고 필요할 때마다 의존성을 변경할 수 있도록 직접 생성 된 인스턴스를 추적하는 것입니다. 그러나 응용 프로그램 전체에서 인스턴스 추적을 유지하는 것이 쉬운 일이 아니기 때문에 그렇게하지 말라고 조언합니다. 이유를 재고하고 해결책을 재고해야합니다. – tucaz

답변

0

strategy pattern을 사용하면 런타임에 동일한 인터페이스의 인스턴스를 쉽게 추적하고 전환 할 수 있습니다. 다음과 같은 유형에 따라 달라집니다

var container = new Container(x => x.Scan(scan => 
{ 
    scan.TheCallingAssembly(); 
    scan.WithDefaultConventions(); 
    scan.AddAllTypesOf<IDiscountCalculator>(); 
})); 
var strategy = container.GetInstance<IDiscountStrategy>(); 
Console.WriteLine(strategy.GetDiscount("Regular", 10)); // 0 
Console.WriteLine(strategy.GetDiscount("Normal", 10)); // 1 
Console.WriteLine(strategy.GetDiscount("Special", 10)); // 5 

: 당신이 바로 처분 할 단명 의존성이있는 경우

public interface IDiscountStrategy 
{ 
    decimal GetDiscount(string userType, decimal orderTotal); 
} 

public class DiscountStrategy : IDiscountStrategy 
{ 
    private readonly IDiscountCalculator[] _discountCalculators; 

    public DiscountStrategy(IDiscountCalculator[] discountCalculators) 
    { 
     _discountCalculators = discountCalculators; 
    } 

    public decimal GetDiscount(string userType, decimal orderTotal) 
    { 
     var calculator = _discountCalculators.FirstOrDefault(x => x.AppliesTo(userType)); 
     if (calculator == null) return 0; 
     return calculator.CalculateDiscount(orderTotal); 
    } 
} 

public interface IDiscountCalculator 
{ 
    bool AppliesTo(string userType); 
    decimal CalculateDiscount(decimal orderTotal); 
} 

public class NormalUserDiscountCalculator : IDiscountCalculator 
{ 
    public bool AppliesTo(string userType) 
    { 
     return userType == "Normal"; 
    } 

    public decimal CalculateDiscount(decimal orderTotal) 
    { 
     return orderTotal * 0.1m; 
    } 
} 

public class SpecialUserDiscountCalculator : IDiscountCalculator 
{ 
    public bool AppliesTo(string userType) 
    { 
     return userType == "Special"; 
    } 

    public decimal CalculateDiscount(decimal orderTotal) 
    { 
     return orderTotal * 0.5m; 
    } 
} 

또는, 당신은 abstract factory에 주입한다 여기에 빠른 예입니다 요구에 따라 작성하십시오.

public ISomeObjectFactory 
{ 
    ISomeObject Create(); 
    void Release(ISomeObject someObject); 
} 

public class SomeObjectFactory 
    : ISomeObjectFactory 
{ 
    //private readonly IAclModule aclModule; 

    // Inject dependencies at application startup here 
    //public SiteMapPluginProviderFactory(
    // IAclModule aclModule 
    // ) 
    //{ 
    // if (aclModule == null) 
    //  throw new ArgumentNullException("aclModule"); 
    // 
    // this.aclModule = aclModule; 
    //} 

    public ISomeObject Create(IState state) 
    { 
     return new SomeObject(state); 
     // return new SomeObject(state, this.aclModule); 
    } 

    pubic void Release(ISomeObject someObject) 
    { 
     var disposable = someObject as IDisposable; 
     if (disposable != null) 
     { 
      disposable.Dispose(); 
     } 
    } 
} 

그리고 사용과 같은 :

public class Consumer : IConsumer 
{ 
    private readonly ISomeObjectFactory someObjectFactory; 

    public Consumer(ISomeObjectFactory someObjectFactory) 
    { 
     if (someObjectFactory == null) 
      throw new ArgumentNullException("someObjectFactory"); 
     this.someObjectFactory = someObjectFactory; 
    } 

    public void DoSomething(IState state) 
    { 
     var instance = this.someObjectFactory.Create(state); 
     try 
     { 
      // Use the instance here. 
     } 
     finally 
     { 
      this.someObjectFactory.Release(instance); 
     } 
    } 
} 

여기에 도시되지는 않았지만, 필요한 경우 공장을 다른 클래스 사이를 전환 할 수있다, 또는 당신이에 다른 종속 (이 예에서 IState)를 통과 할 수 생성 될 때 동일한 유형의 클래스.

Service Locator is Anti-Pattern 매우 희귀 한 경우를 제외하고는 모두 피해야합니다.

관련 문제