代码之家  ›  专栏  ›  技术社区  ›  Rob Packwood

在工厂内运行时使用Ioc容器确定类初始化

  •  2
  • Rob Packwood  · 技术社区  · 16 年前

    这是个好模式吗?让工厂类知道IUnityContainer对我来说有一种代码味道。。。

    我的基本需求是在运行时根据类的Id解析ICalculationRuleProcess。可能是基于身份证以外的东西,我知道。。。基本上,我有一组已知的ID需要处理,因为我手动将记录引导到数据库中,无法编辑记录。每个Id都有一个相关的类。在实现ICalculationRuleProcess的每个类中,我也有不同数量的构造函数参数,因此使用IoC容器与使用Activator.CreateInstance的一些疯狂的switch语句和变量构造函数相比是非常有用的

    以下是我所做的:

    1. 在容器本身中注册了IUnityContainer实例。我不确定这是否可行,但它奏效了。
    2. 创建了一个工厂来确定正确的ICalculationRuleProcess,并让它使用IoC容器来确定要加载的正确类。
    3. 已将工厂类(ICalculationRuleProcessFactory)注册到IoC容器
    4. 在需要使用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 回复  |  直到 16 年前
        1
  •  3
  •   Jay    16 年前

    考虑到你所描述的限制,我觉得这没问题。最重要的是你所有的规则都要执行 ICalculationRuleProcess 所有这些规则的使用者只知道这个接口。

    工厂采用容器依赖性并不是天生的坏事,尤其是作为一个接口。如果你曾经 要更改容器实现,可以创建 IUnityContainer

    Register , Resolve , 方法,并创建一个将这些方法转发给Unity的实现。

        2
  •  2
  •   Igor Zevaka    16 年前

    有另一种方法可以实现这一点,而不依赖于工厂 IUnityContainer ,这本身并不坏。这只是思考问题的另一种方式。

    1. 注册的所有不同实例 ICalculationRuleProcess .
    2. 计算规则流程
    3. 注册 ICalculationRuleProcessFactory 有一份 计算规则流程 创建lambdas。
    4. ICalculationRuleProcessFactory.Create 返回正确的进程。

    现在最棘手的部分是保留注册时使用的ID。一个简单的解决方案是将Id保持在 ICalculationProcess

    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,而不是一个架构上漂亮的解决方案。但不管怎样,你都可以从中获得灵感。

        3
  •  0
  •   Ian Warburton    16 年前

    嘿,罗伯,我打算用基本相同的模式。我有多种类型的购物车项目,需要与它们自己特定的一组不同类的验证器实例相关联。

    简而言之,这些关联可能不是在应用程序运行之前设置的整个程序结构的一部分,因此不应该在应用程序根目录中定义。