2010-07-14 23 views
2

這是一個好的模式嗎?它有一個代碼味道讓我有一個工廠類知道IUnityContainer ...在工廠運行時使用Ioc容器來確定類的初始化

我的基本需求是在運行時根據類的Id解析ICalculationRuleProcess。它可能基於除Id之外的其他內容,我知道這一點......基本上我有一套我需要處理的已知ID,因爲我手動將記錄引導到數據庫中,並且無法編輯記錄。每個ID我有一個相關的類。我也有不同數量的每一個實現ICalculationRuleProcess,所以使用IoC容器類中構造函數的參數是與使用Activator.CreateInstance

這裏是一些瘋狂的switch語句和變量構造aguments非常有用的是我做過什麼:

  1. 在容器本身內註冊了IUnityContainer實例。我不確定這是否可能,但它的工作。
  2. 所有已註冊的ICalculationRuleProcess類的與註冊()內的每個可能的DistributionRule基本上只是Id.ToString()的唯一標識符
  3. 創建一個工廠,以確定正確ICalculationRuleProcess,並且有它使用IoC容器到找出要加載的正確類。
  4. 註冊的工廠類(ICalculationRuleProcessFactory)的IoC容器
  5. 無論在ICalculationRuleProcess需要使用,我有類採取ICalculationRuleProcessFactory在其構造和使用它調用Create方法找出使用哪個ICalculationRuleProcess。

爲出廠時的代碼是在這裏:

public interface ICalculationRuleProcessFactory 
    { 
    ICalculationRuleProcess Create(DistributionRule distributionRule); 
    } 

    public class CalculationRuleProcessFactory : ICalculationRuleProcessFactory 
    { 
    private readonly IBatchStatusWriter _batchStatusWriter; 
    private readonly IUnityContainer _iocContainer; 

    public CalculationRuleProcessFactory(
     IUnityContainer iocContainer, 
     IBatchStatusWriter batchStatusWriter) 
    { 
     _batchStatusWriter = batchStatusWriter; 
     _iocContainer = iocContainer; 
    } 

    public ICalculationRuleProcess Create(DistributionRule distributionRule) 
    { 
     _batchStatusWriter.WriteBatchStatusMessage( 
     string.Format("Applying {0} Rule", distributionRule.Descr)); 

     return _iocContainer.Resolve<ICalculationRuleProcess>(
     distributionRule.Id.ToString()); 
    } 
    } 

回答

3

這似乎還好我,給你描述的約束。最重要的是,你的所有規則都實現了ICalculationRuleProcess,並且這些規則的所有消費者只知道該接口。

您的工廠採用容器依賴性,特別是作爲接口,這並不是固有的壞處。考慮如果你有更改容器的實現,你可以創建一個完全不使用Unity的IUnityContainer實現(只需將接口的所有成員轉發到替換容器中相應的方法)。

如果你感到困擾,您可以通過必要的RegisterResolve方法創建一個不可知的IoC接口增加一個間接的又一層,並創建這些轉發到Unity的實現。

+1

+1我總是最終將'UnityContainer'封裝在我自己的界面中,部分原因是由於某些方法是擴展方法,因此不能通過'IUnityContainer'接口單元測試 – 2010-08-13 00:47:52

0

嘿羅布,我打算使用基本相同的模式。我有多種類型的購物車項目需要與他們自己特定的不同類別的驗證器實例相關聯。

我認爲這種模式有一種氣味,它不是工廠有一個IoC容器的引用,它通常是一個IoC容器配置在應用程序根目錄(通常是UI層)。如果創建一個瘋狂的自定義工廠來處理這些關聯,那麼它可能應該在域中。

簡而言之,這些關聯可能不是在應用程序運行之前設置的總體程序結構的一部分,因此不應在應用程序根目錄中定義。

2

還有另一種方法可以實現這一點,而不需要工廠依賴於IUnityContainer,這本身並不壞。這只是考慮問題的一種不同方式。

的流程如下:

  1. 註冊的ICalculationRuleProcess所有不同的實例。
  2. 獲取所有註冊的ICalculationRuleProcess併爲每個創建一個創建lambda。
  3. 註冊ICalculationRuleProcessFactory與創建lambdas的列表ICalculationRuleProcess
  4. ICalculationRuleProcessFactory.Create返回正確的過程。

現在這個棘手的部分是保存註冊製作的ID。一旦解決方案是簡單地將Id保存在ICalculationProcess界面上,但它可能在語義上不屬於那裏。這就是這個解決方案陷入醜陋的地方(這更多的是在Unity中缺少的功能)。但是,使用擴展方法和小型額外類型,它在運行時看起來很不錯。

所以我們在這裏做的是創建一個擴展方法,返回所有註冊與他們的名字。

public class Registration<T> where T : class { 
    public string Name { get; set; } 
    public Func<T> CreateLambda { get; set; } 

    public override bool Equals(object obj) { 

     var other = obj as Registration<T>; 
     if(other == null) { 
      return false; 
     } 


     return this.Name == other.Name && this.CreateLambda == other.CreateLambda; 
    } 


    public override int GetHashCode() { 
     int hash = 17; 
     hash = hash * 23 + (Name != null ? Name.GetHashCode() : string.Empty.GetHashCode()); 
     hash = hash * 23 + (CreateLambda != null ? CreateLambda.GetHashCode() : 0); 
     return hash; 
    } 


} 

public static class UnityExtensions { 
    public static IEnumerable<Registration<T>> ResolveWithName<T>(this UnityContainer container) where T : class { 
     return container.Registrations 
      .Where(r => r.RegisteredType == typeof(T)) 
      .Select(r => new Registration<T> { Name = r.Name, CreateLambda =()=>container.Resolve<T>(r.Name) }); 
    } 
} 

public class CalculationRuleProcessFactory : ICalculationRuleProcessFactory 
    { 
    private readonly IBatchStatusWriter _batchStatusWriter; 
    private readonly IEnumerable<Registration<ICalculationRuleProcess>> _Registrations; 

    public CalculationRuleProcessFactory(
     IEnumerable<Registration<ICalculationRuleProcess>> registrations, 
     IBatchStatusWriter batchStatusWriter) 
    { 
     _batchStatusWriter = batchStatusWriter; 
     _Registrations= registrations; 
    } 

    public ICalculationRuleProcess Create(DistributionRule distributionRule) 
    { 
     _batchStatusWriter.WriteBatchStatusMessage( 
     string.Format("Applying {0} Rule", distributionRule.Descr)); 

     //will crash if registration is not present 
     return _Registrations 
     .FirstOrDefault(r=>r.Name == distributionRule.Id.ToString()) 
     .CreateLambda(); 
    } 
    } 

//Registrations 
var registrations = container.ResolveWithName<ICalculationRuleProcess>(container); 
container.RegisterInstance<IEnumerable<Registration<ICalculationRuleProcess>>>(registrations); 

當我寫這篇文章之後,我意識到這是比建築上漂亮的解決方案更有創意的lambda douchebaggery。但無論如何,請隨時從中獲得創意。

相關問題