MVC,DI和模拟对象

时间:2010-02-03 16:57:12

标签: model-view-controller dependency-injection

我正试图用尽可能多的有用的抽象来实现MVC,因为我可以用于自动化单元测试。在这个过程中,我遇到了一个有趣的难题:我如何以及在何处为我的数据库声明模拟对象?

这就是我所拥有的。

  • ContactView 是一个实现 IContactView 的表单。它具有 IContactModel 的知识,因此它可以更新自身以响应来自模型对象的事件通知。
  • 联系是一个隐含 IContactModel 的类。它封装了操作对象的业务规则,以及从数据访问层中获取/更新数据的代码。
  • ContactController 是一个实现 IContactController 的类,并且知道 IContactModel IContactView
  • 数据库是一个实现 IDatabase 的类,包含在数据库中选择,插入,更新和删除数据的方法。

对我来说,这是棘手的部分。模型对象需要知道如何与 IDatabase 进行交互,以便它可以在真实数据库或模拟对象上运行。我面临的问题是如何提供对数据库的引用而不违反分离关注。

我宁愿视图和控制器不知道数据是如何存储的。这样,如果我之后选择这样做,我可以将IDatabase替换为IJsonStore或IXmlStore之类的东西,只需要触摸模型类。我不想要我的观点和控制器对数据存储的位置和方式做出任何假设。

我看到了几个可能的解决方案,但我不确定最好的解决方案是什么。

  • 我可以声明一个公开属性的单例,数据库(类型 IDatabase )。这样,单元测试可以将数据库设置为模拟对象,生产代码将其设置为生产数据库。但这意味着生产代码在某些时候必须了解IDatabase。我想这是不可避免的;但我希望有一些其他解决方案更可取。
  • 我可以修改模型类以维护对数据库的引用,并将其置于构造函数中。这似乎是不可取的,因为它会产生大量额外的代码。而且,我正好回到原点:谁声明模型实例必须知道我想要使用哪个 IDatabase 对象。

我确信那里有其他选择,我只是没有意识到它们。所以我把它扔到那里:你们怎么做到这一点,你们看到哪些效果很好?

提前致谢。

3 个答案:

答案 0 :(得分:1)

通常最好将Model对象保持为POCO / POJO,并让控制器使用注入的依赖项填充Model(和View)。

由于许多不同的原因,构造函数注入是DI的最佳默认选择。这是一个基于C#的Controller示例,它带有一个注入的IDatabase:

public class ContactController : IContactController
{
    private readonly IDatabase db;

    public ContactController(IDatabase db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public IContactView CreateView(int id)
    {
        var model = this.db.Select(id);
        return new ContactView(model);
    }
}

我不知道这是否与您现有的界面相似,但它应该足以让您有所了解。注意readonly关键字和Guard子句如何协作使注入的依赖项成为ContactController的不变量,以便保证其余的代码始终存在。

您可以使用穷人的DI 或正确的DI容器在应用程序的入口点连接您的应用程序。这是您将IDatabase映射到具体实现的位置,允许您在其余代码中遵守Liskov Substitution Principle

答案 1 :(得分:1)

正如您所指出的,您必须将持久性连接到模型。如果你认为这是一个依赖项(模型只是没有它就无法运行),那么将它传递给构造函数。这告诉全世界模型必须能够访问持久性,而不是创建模型,直到你拥有它需要的一切。

表示构造函数似乎不是一个属性,而是一个更安全的代码。

其次,我不会称那件事为IDatabase数据库这个词描述的是实现,而不是角色。根据您的域名,我可以将其称为AddressBookRolodex,或者告诉我有关应用程序上下文的内容。这使模型代码不受任何技术实现细节的影响。如果我使用数据库来实现它,我可能会调用类DatabaseAddressBook。我经常发现,对这种分离的挑剔产生更清晰的代码和(有时)更清晰的思考。

答案 2 :(得分:0)