我正在编写一个库,为其消费者提供一系列公共类型。
我想从这个库依赖注入友好中创建类型。这意味着每个类都需要一个构造函数,通过该构造函数可以指定要初始化的对象的每个依赖项。我还希望库遵循约定优于配置原则。这意味着如果消费者想要默认行为,他可以使用无参数构造函数,并且该对象将以某种方式为自己构造依赖关系。
在示例(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>();
}
...
但是,我对此有疑问。服务定位器已被标记为反模式(link),我完全赞同这些论点。另一方面,Martin Fowler主张在这种情况下使用服务定位器模式(库项目)(link)。我想要小心并消除在显示服务定位器确实是一个坏主意之后重写库的可能性。
总而言之 - 您认为服务定位器在这种情况下是否正常?我应该以完全不同的方式解决我的问题吗?任何想法都是受欢迎的......
答案 0 :(得分:4)
如果您想让未使用DI容器的用户更轻松地生活,您可以通过专用Defaults
类提供默认实例,该类具有以下方法:
public virtual Samurai CreateDefaultSamurai()
{
return new Samurai(CreateDefaultWeapon());
}
public virtual IWeapon CreateDefaultWeapon()
{
return new Shuriken();
}
这样您就不需要使用默认构造函数污染类本身,并且您的用户不会无意中使用这些默认构造函数。
答案 1 :(得分: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();
}
}
答案 2 :(得分:1)
我在C#项目中使用了以下方法。目标是实现依赖注入(用于单元/模拟测试),同时不会削弱正常用例的代码实现&#34; (即通过执行流级联有大量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,但也意味着任何消费者都不必担心&#34;默认实例流&#34;应该是。