6

我正在编写一个库,它将向其消费者提供一个公共类型的集合。库项目中构造函数参数的缺省值

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

在示例(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)。我想要小心一点,并避免在显示服务定位器确实是个坏主意后重写库的可能性。

所以最后 - 你认为服务定位器在这种情况下罚款吗?我应该以完全不同的方式解决我的问题吗?任何想法是值得欢迎...

回答

4

如果你想对谁不使用DI容器,可以提供通过专用Defaults类有这样的方法默认情况下,用户的生活更轻松:

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

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

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

+2

保持DI类友好很重要。 +1 – 2012-03-29 18:05:13

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(); 
    } 
} 

而且因为这是一个本地默认(从不同的装配而不是一个,所以它不是一个“混蛋注射液”),你可以使用它作为你的武士班的一部分,以及:

public class Samurai 
{ 
    private readonly IWeapon _weapon; 

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

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

这看起来像一个非常优雅的选择。我只想再问一个问题。 – Tomas 2012-03-29 07:52:07

+0

现在我必须在Samurai类的构造函数中将IWeaponProvider的绑定硬编码为DefaultWeaponProvider。你看到一种方式如何将这个绑定重构到某个组合模块?和IOC容器一样,你有一个单独的配置文件,你可以在其中定义类似于“绑定()”的内容。();“ ( - 来自Ninject的例子)。即使没有办法做到这一点,我会接受你的答案。只是想澄清...谢谢 – Tomas 2012-03-29 08:00:19

+0

只是为了澄清:通过“约定配置”你的意思是你*仍然*想配置这些默认值将是什么?但是,是的,你可以实现你的'DefaultWeaponProvider'来将它的'IWeapon'绑定到任何东西上,即基于配置文件或者IoC容器的内部使用 - 但是你的'Samurai'类不会有任何这些依赖关系 – BrokenGlass 2012-03-29 14:01:05

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,但也意味着任何消费者不必担心“默认实例流”应该是什么。