7

我正在编写一个库,它将为其消费者提供公共类型的集合。

我想让这个库依赖注入中的类型友好。这意味着每个类都需要有一个构造函数,通过它可以指定被初始化对象的每个依赖项。我还希望库遵守配置原则的约定。这意味着如果消费者想要默认行为,他可以使用无参数构造函数,并且对象将以某种方式为自己构造依赖关系。

在示例(C#)中:

public class Samurai {

    private readonly IWeapon _weapon;

    // consumers will use this constructor most of the time
    public Samurai() {
        _weapon = ??? // get an instance of the default weapon somehow
    }

    // consumers will use this constructor if they want to explicitly
    //   configure dependencies for this instance
    public Samurai(IWeapon weapon) {
        _weapon = weapon;
    }
}

我的第一个解决方案是使用服务定位器模式。

代码如下所示:

...
public Samurai() {
    _weapon = ServiceLocator.Instance.Get<IWeapon>();
}
...

不过,我对此有疑问。服务定位器已被标记为反模式(链接),我完全同意这些论点。另一方面,Martin Fowler 提倡在这种情况下使用服务定位器模式(图书馆项目)(链接)。我想小心并消除在显示服务定位器确实是个坏主意后重写库的可能必要性。

所以总而言之 - 你认为服务定位器在这种情况下很好吗?我应该以完全不同的方式解决我的问题吗?欢迎任何想法......

4

3 回答 3

4

如果您想让不使用 DI 容器的用户的生活更轻松,您可以通过Defaults具有如下方法的专用类提供默认实例:

public virtual Samurai CreateDefaultSamurai()
{
   return new Samurai(CreateDefaultWeapon());
}

public virtual IWeapon CreateDefaultWeapon()
{
   return new Shuriken();
}

这样您就不需要使用默认构造函数污染类本身,并且您的用户不会有无意中使用这些默认构造函数的风险。

于 2012-03-29T09:51:42.810 回答
1

有一种替代方法,即注入一个特定的提供者,假设WeaponProvider你的情况是一个到你的类中,这样它就可以为你进行查找:

public interface IWeaponProvider
{
    IWeapon GetWeapon();
}

public class Samurai
{
    private readonly IWeapon _weapon;

    public Samurai(IWeaponProvider provider)
    {
        _weapon = provider.GetWeapon();
    }
}

现在您可以为武器提供本地默认提供程序:

public class DefaultWeaponProvider : IWeaponProvider
{
    public IWeapon GetWeapon()
    {
        return new Sword();
    }
}

由于这是本地默认值(与来自不同程序集的默认值相反,因此它不是“混蛋注入”),您也可以将其用作 Samurai 类的一部分:

public class Samurai
{
    private readonly IWeapon _weapon;

    public Samurai() : this(new DefaultWeaponProvider())
    {
    }

    public Samurai(IWeaponProvider provider)
    {
        _weapon = provider.GetWeapon();
    }
}
于 2012-03-28T23:19:21.033 回答
1

我在我的 C# 项目中使用了以下方法。目标是实现依赖注入(用于单元/模拟测试),同时不削弱“正常用例”的代码实现(即通过执行流程级联的大量 new())。

public sealed class QueueProcessor : IQueueProcessor
{
    private IVbfInventory vbfInventory;
    private IVbfRetryList vbfRetryList;

    public QueueProcessor(IVbfInventory vbfInventory = null, IVbfRetryList vbfRetryList = null)
    {
        this.vbfInventory = vbfInventory ?? new VbfInventory();
        this.vbfRetryList = vbfRetryList ?? new VbfRetryList();
    }
}

这允许 DI,但也意味着任何消费者都不必担心“默认实例流”应该是什么。

于 2014-06-12T16:50:44.907 回答