代码之家  ›  专栏  ›  技术社区  ›  Paul

不可变类和统一应用程序块

  •  2
  • Paul  · 技术社区  · 15 年前

    我正在为自己编写一个小实用程序,所以当我目前使用Unity时,如果它允许我解决这个问题,我可以换一个不同的IOC容器。

    我想创建一个类,它在创建时会提供一些数据,但在创建之后是不可变的,通常我会使用以下方法进行此操作:

    class SomeItem
    {
        public SomeItem(string name)
        {
            Name = name;
            Hash = new SomeHashingClass().GenerateHash(name);
        }
    
        public string Name { get; private set; }
    
        public string Hash { get; private set; }
    }
    

    问题在于构造函数中对somehashingclass的依赖性,如何在保持对象不变的同时注入它?

    一种方法是让构造函数只接受依赖项,然后使用与当前构造函数相同的方法来完成实际工作。然而,我讨厌这个想法,因为它可能使对象处于现有状态,但完全无效。并且需要编写代码以确保方法只能调用一次。

    另一种方法是创建Unity解析的“SomeItemFactory”类,然后手动为SomeItem执行依赖项注入,但是这会使代码量加倍:

    class SomeItemFactory
    {
        IHashingClass _hashingClass;
    
        public SomeItemFactory(IHashingClass hashingClass)
        {
            _hashingClass = hashingClass;
        }
    
        public SomeItem Create(string name)
        {
            return new SomeItem(_hashingClass, name);
        }
    }
    
    class SomeItem
    {
        public SomeItem(IHashingClass hashingClass, string name)
        {
            Name = name;
            Hash = hashingClass.GenerateHash(name);
        }
    
        public string Name { get; private set; }
    
        public string Hash { get; private set; }
    }
    

    请告诉我有一个干净的方法来做这件事。为什么没有这样的方法:

    unityContainer.Resolve<SomeItem>("the items name");
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Community CDub    8 年前

    虽然您确实可以将参数传递给resolve方法,但是如果这实际上是正确的设计,请仔细考虑。

    你为什么一开始就想这么做?是因为您想使用Unity作为服务定位器吗?这是我唯一能想到的原因,但是 I consider this an anti-pattern .

    DI容器的使用应遵循好莱坞的原则:告诉它 resolve the entire application graph at the Composition Root 然后把这一切都忘了。

    在您的特定情况下,您可以保持某些项不变,但如果您希望能够将hashingClass作为依赖项来改变,则需要将其注入到某个项中,并且 构造器注入 是最好的选择。

    如果您的应用程序中只需要一个someitem实例,那么您可以像在Unity中那样连接它,但是如果您需要创建多个实例,那么 Abstract Factory is the correct approach .

    您的例子就差不多了:您只需要从someitemFactory提取一个接口,并在任何需要创建someitem实例的使用者中依赖于这个isomeitemFactory。

    它看起来像是更多的代码,但是代码行在任何情况下(不管是以何种方式)都不是代码质量的特别好的度量标准。但是,这种方法允许您遵循 Single Responsibility Principle 并且独立地改变某个项目的创建和散列。

    注意,没有 these principles 专门针对统一,但一般适用于DI。

        2
  •  0
  •   Community CDub    8 年前

    似乎可以将参数传递给Unity的resolve()方法。有关详细信息,请参阅此问题的已接受答案: Can I pass constructor parameters to Unity's Resolve() method?

        3
  •  0
  •   bristows    15 年前

    你说的团结一致是可以做到的。

    unityContainer.Resolve<ISomeInterface>("MyMappingName");
    

    在config中,假设您使用的是config文件,则可以

    <typeAliases>
        <typeAlias alias="SomeInterface" type="ISomeInterface, SomeAssembly" />
        <typeAlias alias="SomeConcreteType" type="SomeType, SomeAssembly" />
    </typeAliases>
    
    <containers>
        <container>
            <type type="SomeInterface" mapTo="SomeConcreteType" name="MyMappingName" />
        </container>
    </containers>