Подтвердить что ты не робот

Шаблон репозитория. Почему именно нам нужны интерфейсы?

Я прочитал из Интернета. Я получил эти точки, в которых говорится, что для этого используются интерфейсы

  • Использовать методы 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();

Пожалуйста, объясните мне точно, а также другую полезность интерфейса в отношении шаблона репозитория.

4b9b3361

Ответ 1

Итак, как интерфейсы попадают в картину?

Вот так:

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:

  • Установите Ninject.MVC3 NuGet
  • В сгенерированном коде ~/App_Start/NinjectWebCommon.cs вы просто решаете использовать реализацию EF с одной строкой кода:

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

Таким образом вам больше не нужно выполнять какие-либо ручные инстанцирования этих классов репозитория и беспокоиться о повышении или любом другом. Это управляющая среда зависимостей, которая управляет ими для вас, и позаботится о введении определенной реализации в конструктор контроллера.

И просто изменив эту конфигурацию, вы можете переключить технологию доступа к данным, не касаясь одной строки кода в контроллере. Таким образом, модульное тестирование в изоляции также вступает в игру. Поскольку ваш код контроллера теперь слабо связан с репозиторием (благодаря интерфейсу, который мы ввели), все, что вам нужно сделать в unit test, это предоставить некоторую макетную реализацию в репозитории, которая позволяет вам определить ее поведение. Это дает возможность unit test действию контроллера индекса без какой-либо зависимости от базы данных или чего-то еще. Полная изоляция.

Я также приглашаю вас проверить следующие статьи о TDD и DI в ASP.NET MVC.

Ответ 2

Вы можете открыть свой репозиторий в качестве интерфейса:

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

Секретный соус - это factory или другой механизм, с помощью которого можно разрешить интерфейс в пригодный для использования тип (инфраструктура Injection Dependency, такая как Ninject или Castle Windsor, будет выполнять эту роль).

Дело в том, что потребительский код не заботится о реализации, только контракт (интерфейс). Это позволяет легко заменять реализации для целей тестирования и облегчает свободное соединение.

Чтобы уточнить, нет никакой связи между использованием интерфейсов и шаблоном репозитория, это просто другой шаблон, который может их использовать.