我完全理解了Repository模式的设计思想。但为什么我们需要实现iDepository接口类?这是什么特别的用途? 存储库类本身在没有接口类的情况下工作。存储库模式的接口有什么特别的用处?
我认为有人会回答我这是从业务逻辑和数据逻辑的解耦。 但即使没有接口类,是不是数据逻辑解耦的数据逻辑?
我完全理解了Repository模式的设计思想。但为什么我们需要实现iDepository接口类?这是什么特别的用途? 存储库类本身在没有接口类的情况下工作。存储库模式的接口有什么特别的用处?
我认为有人会回答我这是从业务逻辑和数据逻辑的解耦。 但即使没有接口类,是不是数据逻辑解耦的数据逻辑?
这样,您可以在单元测试业务层时注入IRepository类的测试双精度。这具有以下好处:
单元测试是通过构造函数注入来注入测试的一种方法。假设仓库中有以下方法:
void Add(Noun noun);
int NumberOfNouns();
这是你的业务类的代码:
public class BusinessClass {
private IRepository _repository;
public BusinessClass(IRepository repository) {
_repository = repository;
}
// optionally, you can make your default constructor create an instance
// of your default repository
public BusinessClass() {
_repository = new Repository();
}
// method which will be tested
public AddNoun(string noun) {
_repository.Add(new Noun(noun));
}
}
,而无需真正的库测试AddNoun,你需要建立一个测试双。通常你会通过使用模拟框架(如Moq)来实现这一点,但为了说明这个概念,我会从头开始编写一个模拟类。
public IRepository MockRepository : IRepository {
private List<Noun> nouns = new List<Noun>();
public void Add(Noun noun) {
nouns.Add(noun);
}
public int NumberOfNouns() {
return nouns.Count();
}
}
现在你的一个测试可能是这样的。
[Test]
public void AddingNounShouldIncreaseNounCountByOne() {
// Arrange
var mockRepository = new MockRepository();
var businessClassToTest = new BusinessClass(mockRepository);
// Act
businessClassToTest.Add("cat");
// Assert
Assert.AreEqual(1, mockRepository.NumberOfNouns(), "Number of nouns in repository should have increased after calling AddNoun");
}
这样做的结果是,您现在已经测试了BusinessClass.AddNoun方法的功能,而无需触摸数据库。这意味着即使您的存储库层存在问题(例如连接字符串存在问题),您可以确保您的业务层按预期工作。这包括上面的第1点。
至于上面的第2点,无论何时您正在编写测试数据库的测试,您都应该确保它在每次测试之前处于已知状态。这通常包括在每次测试开始时删除所有数据并重新添加测试数据。如果没有这样做,那么你就不能针对表格中的行数运行断言,因为你不能确定它应该是什么。
删除和重新添加测试数据通常是通过运行SQL脚本来完成的,这些脚本在数据库结构发生变化时很慢并且容易破损。因此,建议将数据库的使用仅限于存储库本身的测试,并在单元测试应用程序的其他方面时使用模拟出的存储库。
至于抽象类的使用 - 是的,这将提供相同的能力来提供测试双打。我不确定你会选择将哪些代码放在抽象基础中,哪些是具体的实现。 This answer to an SO question关于抽象类vs交互有一个有趣的讨论。
首先,您必须了解Repository模式是什么。这是一个抽象层,因此应用程序的其余部分不必关心数据来自何处。
.NET中的抽象通常由接口表示,因为没有逻辑(代码)可以附加到接口。
作为奖励,该接口也使得它更容易为你测试你的应用程序,因为你可以很容易mock接口(或创建一个stub)
该接口还可以让你发展你的数据层。例如,您可能首先使用所有存储库类的数据库。但后来你想要在Web服务背后移动一些逻辑。然后你只需要用WCF仓库替换数据库仓库。您可能还会发现存储库很慢,想要在其中实现一个简单的内存缓存(通过使用memcache或其他内容)
如何使用抽象类? 我正在为包含不同语言(例如英语,法语,意大利语,日语)的字典创建存储库类... 虽然每种语言都有很不同的属性,但它们确实共享一些常用属性,例如名词,动词,等等。对于一些函数,像GetEntry一样,但GetPartofSpeech会有很大的不同。为了减少代码,我应该使用接口还是抽象类? –
实体本身可以包含任何你喜欢的东西。存储库的目的是为这些实体构建和填充信息。因此从存储库的角度来看,实体是否继承基类并不重要。这是应该实现接口的存储库本身。 – jgauffin
我发现了一个非常有用的msdn页面,展示了存储库和测试驱动开发 的想法。 http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx
如何实现1和2?我还是不明白。你可以平行比较使用接口和抽象类吗?实际上,接口不允许执行方法,我觉得这是浪费代码。 –
我已经扩展了单元测试业务层使用测试双打。 –