【问题标题】:Dependency Injection - When to use property injection依赖注入 - 何时使用属性注入
【发布时间】:2013-09-17 18:17:47
【问题描述】:

我有一个类,它有这样的构造函数:

    private string _someString;
    private ObjectA _objectA;
    private ObjectB _objectB;
    private Dictionary<Enum, long?> _dictionaryA;
    private Dictionary<Tuple<Enum,long?>, long?> _dictionaryB; 

    public SomeDiClass(string someString)
    {
        _someString = someString;

        _objectA = new ObjectA();
        _objectB = new ObjectB();

        _dictionaryA = new Dictionary<Enum, long?>();
        _dictionaryB = new Dictionary<Tuple<Enum, long?>, long?>();
    }

我想从这个构造函数中创建依赖项。在第一步中,我会将 ObjectA 和 B 依赖项移动到构造函数参数中,以通过构造函数注入来注入它们。我想为此目的使用 IoC 容器,这就是我目前停留的地方。问题是如何处理 someString 和字典。我需要将它们注入到类中,因为字典的内容将是单元测试的重要组成部分。 通过属性注入注入字符串和字典是否是个好主意(我在其他类中不需要它们),所以我最终会得到这样的东西?:

    private ObjectA _objectA;
    private ObjectB _objectB;

    public string SomeString { get; set; }
    public Dictionary<Enum, long?> DictionaryA { get; set; }
    public Dictionary<Tuple<Enum, long?>, long?> DictionaryB { get; set; }

    public SomeDiClass(ObjectA objectA, ObjectB objectB)
    {
        _objectA = objectA;
        _objectB = objectB;
    }

是否有解决此类问题的最佳实践?

【问题讨论】:

  • 当你说 ioc 时?你说的是spring,java框架吗?还是另一件事?泰。
  • 一般来说。目前的项目是 C#,所以我最终会得到 Unity 之类的东西。

标签: c# dependency-injection ioc-container


【解决方案1】:

依赖注入不是最终目标,而是特定问题集的解决方案。例如,依赖注入使替换单元测试的抽象变得容易,并使您的应用程序更加灵活,因为您可以交换、装饰和拦截依赖项,而无需更改使用类。可以在本书Dependency Injection Principles, Practices, and Patterns (DIPP&P) 的免费获得的chapter 1 中找到对依赖注入的良好介绍。

这并不意味着您应该注入一个类所具有的每个依赖项,因为它必须帮助您使类更易于测试并且系统更易于维护。因此,您必须问自己,从测试的角度来看,从外部注入这些字典是否有帮助,或者是否有助于使您的应用程序更加灵活。要很好地掌握要注入的内容和不注入的内容,您应该了解易失性和稳定依赖关系的概念,可以在 DIPP&P 第 1 章的section 1.3 中阅读。

从测试或可维护性的角度来看它是否有帮助,这是一个很难回答的问题,因为您的问题没有足够的细节。但这里有一些提示:

您通常想要注入到类中的唯一内容是服务和配置值。

  • 服务是一些提供“服务”的合同/抽象/接口。这通常意味着该服务将代表您执行某些操作,例如计算价格、与数据库通信、缓存值、返回系统时间或格式化您的硬盘:)

  • 配置值就是它的本来面目;只是一个值。但是你需要注入它——它不能被硬编码到类中,并且你不希望类从ConfigurationManager 中获取值本身,因为这会创建一个隐藏的依赖项(在Configurationmanager),这会使课程更难测试。

其他东西,例如原语、消息、DTO、集合类型和实体,以及任何其他不提供任何服务(业务逻辑)并且不妨碍单元测试的东西,不必抽象,因此不必注入(实际上是shouldn't be injected through the constructor or property)。在您的情况下,字典是 SomeDiClass 类的内部状态的一部分,而不是您的类所依赖的服务。

另一方面,如果这些字典被其他服务重用,则必须注入这些字典。但是你永远不想直接注入这样的字典本身,因为字典本身不是服务。相反,您需要围绕它们创建一个抽象;隐藏该字典的详细信息并为应用程序提供围绕它的服务的东西。

【讨论】:

  • +1 TL;DR,但在阅读 Dependency Injection is not a goal, but a solution to a particular set of problems 之后,我想其余的会详细说明 ;)
  • 我同意你对依赖注入的解释。事实上,我需要模拟我想要注入的类(在本例中为 objA 和 B)。围绕字典包装服务的概念,为班级提供所需的信息,这似乎是一个好主意,谢谢!关于如何注入字符串仍然存在问题。通过属性注入注入它会是更好的方法,还是应该将它保存在 objA 和 B 之外的构造函数参数中?
  • 我个人喜欢将配置值从构造函数中移出(到属性中),因为这会使您的 DI 配置更容易(假设您使用容器),但请注意它确实导致temporal coupling
  • 啊,非常感谢您对时间耦合的提示,这是一篇非常有趣的博客文章。就我而言,我将进行属性注入,因为字符串是否为 nullOrEmpty 并不重要,这已经在类中处理了。所以最后我得到了一个额外的类,它提供了我的字典 objA 和 B 的内容,它们通过构造函数注入(使用 IoC 容器来检索对象)和字符串的属性注入。
【解决方案2】:

当您的类型的对象创建超出您的控制范围时,您应该使用属性注入(或 setter 注入)。像aspx Page、HttpHandler、ApiController等。其他情况推荐使用构造函数注入。

要使用 StructureMap 解决 aspx 页面的依赖关系,我使用以下方法。

首先,我创建一个 BasePage 类并在构造函数中使用 StructureMap 的 BuildUp() 方法来解决派生页面的依赖关系。代码如下:

public class BasePage : Page
{
    public BasePage()
    {
        // instruct StructureMap to resolve dependencies
        ObjectFactory.BuildUp(this);
    }
}

public class Default : BasePage
{
     public ICustomerService customerService { get; set; }

     protected void Page_Load(object sender, EventArgs e)
     {
         // can start using customerService
     }
}

public class Login : BasePage
{
     public IAuthenticationService authenticationService { get; set; }

     protected void Page_Load(object sender, EventArgs e)
     {
         // can start using authenticationService
     }
}

【讨论】:

  • 为什么只在对象创建不受控制时才推荐属性注入?在我控制对象创建时使用属性注入有什么问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
  • 1970-01-01
  • 2012-05-03
  • 1970-01-01
  • 2010-12-07
相关资源
最近更新 更多