2010-07-14 37 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。但无论如何,请随时从中获得创意。

相关问题