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

时间:2010-02-13 18:45:56

标签: c# .net dependency-injection ioc-container immutability

我正在为自己编写一个小工具,所以当我正在使用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 个答案:

答案 0 :(得分:3)

虽然您确实可以将参数传递给Resolve方法,但请仔细考虑这是否真的是正确的设计。

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

DI容器的使用应遵循好莱坞原则:告诉resolve the entire application graph at the Composition Root,然后忘记它。

在您的特定情况下,您可以按原样保留SomeItem,但是如果您希望能够将HashingClass作为依赖项进行更改,则需要将其注入SomeItem,并且构造函数注入是最好的选择。

如果您只需要在应用程序中使用单个SomeItem实例,则可以像在Unity中那样连接它,但如果您需要创建多个实例,则Abstract Factory is the correct approach

您的示例几乎就在那里:您只需从SomeItemFactory中提取一个接口,并在需要创建SomeItem实例的任何使用者中依赖此ISomeItemFactory。

它可能看起来像更多的代码,但在任何情况下(代码或其他方式),代码行都不是特别好的代码质量指标。但是,这种方法允许您遵循Single Responsibility Principle并相互独立地改变SomeItem的创建和散列。

请注意,these principles并非专门针对Unity,而是广泛适用于DI。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

你可以按照统一的说法去做

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

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

<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>