Я знаю, как использовать внедрение зависимостей, но не вижу для этого практического преимущества - PullRequest
6 голосов
/ 08 сентября 2011

Речь идет об этом (вставить зависимость)

private readonly ICustomerService _customerService;
public Billing(ICustomerService customerService)
{
   _customerService = customerService;
}

против этого (Создать зависимость)

private readonly ICustomerService _customerService;
public Billing()
{
   _customerService = new CustomerService();
}

последний пример, поэтому они говорят, что это плохо, потому что ... он нарушает DI ... конечно, ничего не вводится ... но что, если DI не существует, что такого плохого, что CustomerService создается вручную из класса Billing?Я не вижу практического преимущества в отношении взаимозаменяемости интерфейса Сервиса.

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

Кто-нибудь достаточно заинтересован, чтобы показать свои мышцы DI и почему он имеет практическое право на существование и применение?

ОБНОВЛЕНИЕ

Так что люди не имеютчтобы прочитать все, я напишу здесь мой короткий опыт:

DI как шаблон имеет практическое применение.Чтобы следовать DI, не вводя все службы вручную (плохой инструмент DI, как говорят ...), используйте инфраструктуру DI, такую ​​как LightCore / Unity, но убедитесь, что вы используете правильный инструмент для соответствующей работы.Это то, чего я не делал ;-) При разработке приложения mvvm / wpf у меня есть другие требования, которые инструмент LightCore / Unity не мог поддерживать, они даже были барьером.Моим решением было использовать MEFEDMVVM, чем я доволен.Теперь мои сервисы автоматически добавляются во время выполнения, а не во время запуска. :-)

Ответы [ 4 ]

5 голосов
/ 09 сентября 2011

Понимание как и понимание , почему - это очень разные вещи ..

Одним из самых больших преимуществ DI является модульное тестирование. Во втором примере невозможно выполнить модульное тестирование Billing, не протестировав также CustomerService (а также не проверив дальнейшие зависимости в цепочке). В этом случае вы не юнит-тестирование, а интеграционное тестирование! Если вы хотите получить хорошее обоснование для использования DI, вам не нужно смотреть дальше, чем обоснование для модульного тестирования.

4 голосов
/ 09 сентября 2011

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

Теперь вы хотите провести модульное тестирование Billing, чтобы убедиться в правильности вычислений, которые он выполняет (вы не хотите отправлять неправильные счета, верно?)

Как вы будете тестировать модуль Billing, если его конструктор связан с классом, который требует подключения к реальной системе CRM и базе данных? Разве не было бы лучше внедрить эту зависимость в качестве интерфейса, что позволит вам легко предоставить фиктивную версию для ваших тестов?

Именно из-за полезен DI.

3 голосов
/ 09 сентября 2011

DI Полезно, когда вы хотите передать различные реализации интерфейса вашему классу, например: модульное тестирование.

Скажем, ваш конструктор Billing является конструктором контроллера MVC, а ваш CustomerService принял в качестве параметра некоторую форму IDataContext.

Global.asax

// Does the binding
ICustomerService binds to CustomerService
IDataContext binds to EntityFrameworkContext

CustomerService

private IDataContext _datacontext;   
public CustomerService(IDataContext dataContext)   
{   
   _dataContext = dataContext;  
}

public AddCustomer(Customer entity)   
{   
  this._dataContext.Customers.Add(entity);
  this._dataContext.SaveChanges;   
}   

Контроллер MVC

private ICustomerService _customerService;
public Billing(ICustomerService customerService)
{
   _customerService = customerService;
}

public ActionResult NewCustomer()
{
  Customer customer = new Customer(){ Name = "test" };
  this._customerService.AddCustomer(customer);

  return View();
}

Скажем, вы хотели провести модульное тестирование ваших Сервисов или Контроллеров. Вы бы передали CustomerServices, но вы бы передали поддельную реализацию EntityFrameWorkContext. Таким образом, FakeDbContext, который реализует IDataContext, передается в службу поддержки клиентов.

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

1 голос
/ 09 сентября 2011

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

Это, как говорится, иногда боль всегда вводить все.Инъекционные рамки могут облегчить эту нагрузку, но я не большой поклонник.Другой добрый пользователь stackoverflow указал мне на то, что он назвал «инъекция бедного человека».По сути, он состоит из двух перегрузок конструктора: один конструктор с внедренным интерфейсом и один без.Тот, кто без, ничего не делает, кроме как создает конкретный класс, который реализует интерфейс, и передает его другому конструктору.Это выглядит примерно так:

public class Billing
{
  ICustomerService _customerService;

  public Billing():this(new CustomerService()) {}

  public Billing(ICustomerService customerService)
  {
    _customerService = customerService;
  }
}

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

...