49

我从互联网上读到了这一点,上面说接口用于此

  • 使用 TDD 方法
  • 更换持久化引擎

但我无法理解接口在这一点上会有什么用处Replace persistance engine。让我们考虑一下我正在为EmployeeRepository

public class EmployeeRepository
{
  public employee[] GetAll()
  {
     //here I'll return from dbContext or ObjectContex class
  }
}

那么接口是如何出现的呢?

如果假设我创建了一个接口,为什么要使用向上转换?例如

 IEmployee emp = new EmployeeRepository() ;
 vs
 EmployeeRepository emp = new EmployeeRepository();

请准确解释我以及接口在存储库模式方面的其他有用性。

4

2 回答 2

91

那么接口是如何出现的呢?

像这样:

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);
    }   
}

看看控制器如何不再依赖于存储库的特定实现?它只需要知道这个实现尊重合同。现在你需要做的就是配置你喜欢的依赖注入框架来使用你想要的实现。

这是如何使用 Ninject 完成此操作的示例:

  1. 安装Ninject.MVC3 NuGet
  2. 在生成的~/App_Start/NinjectWebCommon.cs代码中,您只需决定将 EF 实现与一行代码一起使用:

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>();
    }        
    

这样,您不再需要对这些存储库类进行任何手动实例化,也无需担心向上转换或其他任何事情。它是为您管理它们的依赖注入框架,并将负责将定义的实现注入到控制器构造函数中。

只需修改此配置,您就可以切换数据访问技术,而无需触及控制器中的任何一行代码。这就是孤立的单元测试也发挥作用的方式。由于您的控制器代码现在与存储库弱耦合(感谢我们引入的接口),您在单元测试中需要做的就是在存储库上提供一些模拟实现,以便您定义其行为。这使您可以对索引控制器操作进行单元测试,而不依赖于数据库或其他任何东西。完全隔离。

我还邀请您查看以下有关 ASP.NET MVC 中的 TDD 和 DI 的文章。

于 2012-05-16T09:57:49.203 回答
15

您会将存储库公开为接口:

public interface IEmployeeRepository
{
    List<Employee> GetAll();
}

这将允许您拥有许多不同的接口实现,例如默认的:

public class EmployeeRepository : IEmployeeRepository
{
    public List<Employee> GetAll()
    {
        // Return from db.
    }
}

或者一个测试:

public class TestEmployeeRepository : IEmployeeRepository
{
    public List<Employee> GetAll()
    {
        // Stub some dummy data.
    }
}

然后,您使用存储库的代码只对使用接口感兴趣:

IEmployeeRepository myRepo = MyRepositoryFactory.Get<IEmployeeRepository>();

秘诀是工厂,或将接口解析为可用类型的另一种机制(依赖注入框架,如 Ninject 或 Castle Windsor 将完成此角色)。

关键是,消费代码不关心实现,只关心合约(接口)。这使您可以非常轻松地更换实现以进行测试,并促进松散耦合。

只是为了澄清一下,接口的使用和存储库模式之间没有具体的联系,它只是另一种可以利用它们的模式。

于 2012-05-16T09:57:36.283 回答