Внедрение зависимостей: отделение графика вызовов от графика построения - PullRequest
1 голос
/ 28 апреля 2009

Я пытаюсь применить принципы внедрения зависимостей , и у меня возникают некоторые трудности с этим.

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

Если один или несколько продуктов признаны небезопасными, мне нужно отправить отзыв, создав экземпляр и отправив объект ProductRecall.

Функция выглядит примерно так: (псевдокод)

void SafteyInspector::CheckProductSaftey()
{
  database.getProducts( list_of_products )

  foreach( list_of_products as p )
  {
    if ( !runBatteryOfTests( p ) )
      unsafe_products.insert( p );
  }

  if ( !unsafe_products.empty() )
  {
    ProductRecall * recall = 
          new ProductRecall( unsafe_products );  // Oh no!
    recall->dispatch();
  }
}

Проблема в том, что я "новичок" - объект ProductRecall прямо в середине моего графа вызовов. Это нарушает принципы внедрения зависимости. Как написано, я не могу протестировать CheckProductSaftey() без создания ProductRecall объекта.

Однако я не могу передать объект ProductRecall в мой объект SafetyInspector, потому что SafetyInspector - это тот, кто определяет список небезопасных продуктов.

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

Есть предложения? Спасибо!

Ответы [ 2 ]

1 голос
/ 28 апреля 2009

Я думаю, что проблема может быть в вашей реализации ProductRecall. В частности, если вы можете вызвать dispatch() для вновь созданного объекта, это подразумевает, что большая часть реальной функциональности либо скрыта внутри класса ProductRecall, либо что класс ProductRecall имеет статические члены и / или синглтоны для его предоставления. все остальное для этого нужно.

Я бы порекомендовал создать класс с именем ProductRecallDispatcher, который обрабатывает сложности реальной отправки. Затем вы должны создать один из этих объектов и передать его конструктору для SafteyInspector. Это позволит вашей функции CheckProductSafety выглядеть следующим образом:

void SafteyInspector::CheckProductSaftey()
{
  database.getProducts( list_of_products )

  foreach( list_of_products as p )
  {
    if ( !runBatteryOfTests( p ) )
      unsafe_products.insert( p );
  }

  if ( !unsafe_products.empty() )
  {
    recallDispatcher.recall( unsafe_products );
  }
}
1 голос
/ 28 апреля 2009

Я думаю, что на самом деле вам нужно вызвать в воображении какую-то ProductRecallFactory, чтобы вместо этого пройти. Обычно контейнеры для инъекций поддерживают какой-то интерфейс в заводском стиле, или вы можете просто сделать так, чтобы ваш контейнер SafetyInspector учитывал.

РЕДАКТИРОВАТЬ: под "контейнером" я имею в виду реализацию некоторого интерфейса в соответствии с IObjectWithSite COM, чтобы ваш объект мог вызывать своего родителя. Это более слабая форма внедрения зависимости, которая частично отменяет инверсию управления. Если вы выполняете внедрение зависимостей вручную, обязательно добавьте объект фабрики.

...