设计依赖注入类的指南

时间:2008-09-24 14:35:16

标签: inversion-of-control

This question about unit testing best practices提到设计依赖注入类。这让我想到了究竟是什么意思。

刚刚开始使用控制容器的反转,我对这个问题有一些想法,所以让我把它们扔在墙上,看看有什么粘性。

我看到它的方式,对象可以有三种基本类型的依赖关系。

  1. 对象依赖 - 相关类将使用的实际对象。例如LogInFormController中的LogInVerifier。这些应该通过构造函数注入。如果类足够高,在构造函数中需要超过4个这些对象,请考虑将其拆分或至少使用工厂模式。您还应该考虑使用接口提供依赖关系并对接口进行编码。
  2. 简单设置 - 例如阈值或超时时间。这些通常应具有默认值,并通过工厂模式的构建器进行设置。您还可以提供设置它们的构造函数重载。但是在大多数情况下,您可能不应该强制客户端必须明确地进行设置。
  3. 消息对象 - 从一个类传递到另一个类的对象,接收类可能用于业务逻辑。一个示例是LogInCompleRouter类的User对象。在这里我发现通常更好的是不在构造函数中指定消息,因为您必须使用IoC容器注册User实例(使其成为全局)或者在您拥有User实例之后不实例化LogInCompleteRouter (你不能使用DI或至少需要对Container的显式依赖)。在这种情况下,最好只在需要方法调用时传入消息对象(即LoginInCompleteRouter.Route(User u);)。
  4. 另外,我应该提一下,所有应该是DI,如果你有一个简单的功能,只是方便分解到一个扔掉的类,它可能没问题在现场实例化。显然这是一个判断力;如果我发现编写像

    这样的类是有利的
    class PasswordEqualsVerifier {
      public bool Check(string input, string actual) { return input===actual;}
    }
    

    我可能不会打扰依赖注入它,只是让一个对象直接在using块中实例化它。其必然结果是,如果值得编写单元测试,则可能值得注入。

    那你们觉得怎么样?欢迎任何其他指南或对比意见。

1 个答案:

答案 0 :(得分:1)

重要的是尝试对接口进行编码,让您的类接受这些接口的实例,而不是自己创建实例。显然你可能会对此感到疯狂,但无论是单元测试还是DI,这都是一般的好习惯。

例如,如果您有数据访问对象,您可能倾向于为所有DAO编写基础,如下所示:

public class BaseDAO
{
    public BaseDAO(String connectionURL, 
                   String driverName, 
                   String username, String password)
    {
        // use them to create a connection via JDBC, e.g.
    }

    protected Connection getConnection() { return connection; }
}

但是,最好从类中删除它以支持接口

public interface DatabaseConnection
{
    Connection getConnection();
}

public class BaseDAO
{
    public BaseDAO(DatabaseConnection dbConnection)
    {
        this.dbConnection = dbConnection;
    }

    protected Connection getConnection() { return dbConnection.getConnection(); }
}

现在,您可以提供DatabaseConnection的多个元素。即使忽略单元测试,如果我们假设我们使用JDBC,有两种方法可以从容器中获取Connection:连接池,或者直接通过使用驱动程序。现在,您的DAO代码未与任何策略相关联。

对于测试,您可以使用固定数据创建一个连接到某些嵌入式JDBC实现的MockDatabaseConnection来测试您的代码。