存储库模式 – 为什么我们需要接口?
我已经从互联网上读到,我得到了这个说Interfaces是用于这个的点
- 使用TDD方法
- 更换持久性引擎
但我无法理解接口将如何有用Replace persistance engine
。 让我们考虑为EmployeeRepository
创build一个基本的(没有generics)仓库
public class EmployeeRepository { public employee[] GetAll() { //here I'll return from dbContext or ObjectContex class } }
那么接口如何进入画面呢?
如果我想创build一个接口,为什么使用upcasting? 例如
IEmployee emp = new EmployeeRepository() ; vs EmployeeRepository emp = new EmployeeRepository();
请详细解释我,还有关于Repository Pattern的其他有用的接口。
那么接口如何进入画面呢?
喜欢这个:
public interface IEmployeeRepository { Employee[] GetAll(); }
然后你可以有很多的实现,只要你喜欢:
public class EmployeeRepositoryEF: IEmployeeRepository { public Employee[] GetAll() { //here you will return employees after querying your EF DbContext } } public class EmployeeRepositoryXML: IEmployeeRepository { public Employee[] GetAll() { //here you will return employees after querying an XML file } } public class EmployeeRepositoryWCF: IEmployeeRepository { public Employee[] GetAll() { //here you will return employees after querying some remote WCF service } } and so on ... you could have as many implementation as you like
正如你所看到的,我们如何实现仓库并不重要。 重要的是,所有的存储库和实现都遵守已定义的合同(接口),并且都拥有一个返回员工列表的GetAll
方法。
然后你将有一个使用这个接口的控制器。
public class EmployeesController: Controller { private readonly IEmployeeRepository _repository; public EmployeesController(IEmployeeRepository repository) { _repository = repository; } public ActionResult Index() { var employees = _repository.GetAll(); return View(employees); } }
看看控制器不再依赖于存储库的特定实现吗? 所有它需要知道的是这个实现尊重合同。 现在你所需要做的就是configuration你喜欢的dependency injection框架来使用你想要的实现。
下面是一个如何用Ninject完成的例子:
- 安装Ninject.MVC3 NuGet
-
在生成的
~/App_Start/NinjectWebCommon.cs
代码中,您只需决定使用~/App_Start/NinjectWebCommon.cs
代码就可以使用EF实现:private static void RegisterServices(IKernel kernel) { kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>(); }
这样你不再需要对这些存储库类进行任何手动实例化,并担心向上转换或其他任何事情。 它是为您pipe理它们的dependency injection框架,并负责将定义的实现注入到控制器构造函数中。
通过简单的修改这个configuration,你就可以切换你的数据访问技术,而不需要触摸你的控制器中的一行代码。 这样一来unit testing的方式也就起作用了。 由于您的控制器代码现在与存储库弱耦合(感谢我们介绍的接口),所以您在unit testing中需要做的是在存储库上提供一些模拟实现,从而允许您定义其行为。 这使您可以unit testing索引控制器的动作,而不依赖于数据库或其他任何东西。 完全隔离。
我还邀请你去看看关于ASP.NET MVC中的TDD和DI的以下文章 。
你会把你的版本库作为一个接口公开:
public interface IEmployeeRepository { List<Employee> GetAll(); }
这将允许你有许多不同的接口实现 ,比如默认的接口:
public class EmployeeRepository : IEmployeeRepository { public List<Employee> GetAll() { // Return from db. } }
或者是testing一:
public class TestEmployeeRepository : IEmployeeRepository { public List<Employee> GetAll() { // Stub some dummy data. } }
您的代码使用库只对使用接口感兴趣:
IEmployeeRepository myRepo = MyRepositoryFactory.Get<IEmployeeRepository>();
秘密就是工厂,或者将接口parsing成可用types的另一种机制(像Ninject这样的dependency injection框架,或者温莎城堡就可以实现这个angular色)。
重点是, 消费代码不关心实现 ,只有合同 (接口)。 这使您可以非常容易地将testing用途换出来,并促进松耦合。
只是为了澄清,接口的使用和存储库模式之间没有任何联系,这只是另一种可以利用它们的模式。