6

这个关于单元测试最佳实践的问题提到了为依赖注入设计类。这让我开始思考这究竟意味着什么。

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

在我看来,一个对象可以拥有三种基本类型的依赖关系。

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

另外,我应该提一下,并不是所有的东西都应该 DI,如果你有一些简单的功能可以方便地分解到一次性类中,那么当场实例化可能是可以的。显然,这是一个判断电话;如果我发现编写一个类如

class PasswordEqualsVerifier {
  public bool Check(string input, string actual) { return input===actual;}
}

我可能不会打扰依赖注入它,只会让一个对象直接在 using 块内实例化它。推论是,如果值得为其编写单元测试,那么它可能值得注入。

那你们怎么看?欢迎任何额外的指导方针或对比意见。

4

1 回答 1

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 代码未与任一策略耦合。

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

于 2008-09-24T14:45:01.503 回答