我正试图用尽可能多的有用的抽象来实现MVC,因为我可以用于自动化单元测试。在这个过程中,我遇到了一个有趣的难题:我如何以及在何处为我的数据库声明模拟对象?
这就是我所拥有的。
对我来说,这是棘手的部分。模型对象需要知道如何与 IDatabase 进行交互,以便它可以在真实数据库或模拟对象上运行。我面临的问题是如何提供对数据库的引用而不违反分离关注。
我宁愿视图和控制器不知道数据是如何存储的。这样,如果我之后选择这样做,我可以将IDatabase替换为IJsonStore或IXmlStore之类的东西,只需要触摸模型类。我不想要我的观点和控制器对数据存储的位置和方式做出任何假设。
我看到了几个可能的解决方案,但我不确定最好的解决方案是什么。
我确信那里有其他选择,我只是没有意识到它们。所以我把它扔到那里:你们怎么做到这一点,你们看到哪些效果很好?
提前致谢。
答案 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
。 数据库这个词描述的是实现,而不是角色。根据您的域名,我可以将其称为AddressBook
或Rolodex
,或者告诉我有关应用程序上下文的内容。这使模型代码不受任何技术实现细节的影响。如果我使用数据库来实现它,我可能会调用类DatabaseAddressBook
。我经常发现,对这种分离的挑剔产生更清晰的代码和(有时)更清晰的思考。
答案 2 :(得分:0)