Служба DI без зависимых служб

Я работаю с Ninject для реализации приложения с использованием внедрения зависимостей. Я чувствую, что у меня есть довольно полное понимание концепций, и мне очень понравилась слабосвязанная и тестируемая архитектура, которую приложение получило с помощью DI. Однако я борюсь с одним конкретным типом службы и ищу информацию о том, делаю ли я что-то неправильно или другие сталкивались с тем же.

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

Это плохой дизайн или какой-то запах кода, который мне не хватает? Очевидно, я мог бы переместить эту функциональность в сам репозиторий, но мне это кажется нарушением SRP (репозиторий имеет полезные функции доступа и т. д. и фактически может быть заполнен несколькими службами). Некоторые подходы, которые я рассматривал или пробовал, но которые меня не устраивают:

  1. Сделайте так, чтобы у службы был один общедоступный метод, например Start, который необходимо вызвать, чтобы она начала работать. У этого есть недостаток, заключающийся в необходимости определения несколько произвольного места в системе для выполнения этого вызова.
  2. Привяжите службу к константе, которую я создаю при создании ядра Ninject. Это требует, чтобы я понимал, что никто не зависит от этой службы, поэтому ее нужно обрабатывать специально, что кажется неправильным.
  3. Добавьте несколько членов в службу и создайте графический интерфейс где-нибудь в моем приложении, который считывает эти значения (например, статус службы и т. д.). Очевидно, что добавление графического интерфейса в мое приложение, которое существует только по этой причине, довольно глупо (хотя иногда это полезно для отладки и т. д.).

Любые мысли или рекомендации?


person StellarEleven    schedule 17.01.2013    source источник


Ответы (2)


Вы говорите, что IMonkeyPopulator зависит от IMonkeyRepository, но кажется, что должно быть наоборот? Похоже, что ваш IMonkeyRepository зависит от IMonkeyPopulator и, следовательно, может нуждаться в инъекциях. Если вы также внедрите какую-то другую службу, хорошо, но внутри IMonkeyRepository может сказать IMonkeyPopulator «запустить», чтобы в репозитории действительно что-то было? Хотя, возможно, я неправильно понимаю проблему... может быть, мне не стоит так много дурачиться :/

person Quanta    schedule 17.01.2013
comment
Интересно, но я думаю, что такой подход нарушит принцип единой ответственности. Скажем, у меня есть несколько заполнителей, мне пришлось бы вводить их все в репозиторий (и запускать их все). Теперь класс репозитория должен будет меняться как каждый раз, когда возникает новый заполнитель, так и когда необходимо изменить способ доступа к элементам в репозитории. Несколько причин для изменения класса указывают на нарушение SRP. При этом я думаю, что этот подход может быть не хуже всего, о чем я думал. - person StellarEleven; 18.01.2013
comment
В этом случае я бы внедрил объект, например IMonkeyPopulatorAggregate, и вызвал бы его метод populate() из репозитория. Если бы мне нужно было добавить больше заполнителей, код репозитория вообще не изменился бы. Сказав это, по моему опыту, планирование для всех возможных непредвиденных обстоятельств, а не для конкретной проблемы (и, возможно, сценарий, который вы упомянули, ЯВЛЯЕТСЯ вашей конкретной проблемой - я не знаю), похоже на поиск дна кроличьей норы. И, наконец, как только вы действительно поймете конкретное правило/принцип/паттерн/философию/аромат месяца, оставляйте за собой право нарушать его. - person Quanta; 19.01.2013

Таким образом, ваш IMonkeyPopulator является чем-то вроде ActiveObject, который прослушивает TCP-соединение в фоновом режиме и записывает эти данные в репозиторий. Я бы сказал с точки зрения приложения, что активный объект должен быть запущен и остановлен, потому что вы не хотите запускать TCP-соединение в конструкторе. Таким образом, вы можете использовать методы OnActivation и OnDeactivation в привязке для запуска и остановки службы следующим образом:

 This.Bind<IPopulatorService>().To<>().OnActivation((c, i) => i.Start()).OnDeactivation((c, i) => i.Stop())

Но все же кто-то в вашем приложении должен получить/получить IPopulatorService, чтобы создать его экземпляр в вашем приложении. Обычно я использую шаблон начальной загрузки здесь http://www.appccelerate.com/bootstrapper.html в чтобы добиться этого.

person Daniel Marbach    schedule 17.01.2013
comment
Почему вы сказали, что я не хотел бы запускать TCP-соединение в конструкторе? - person StellarEleven; 18.01.2013
comment
Я много практикую TDD, и это облегчает, если ваши объекты легко построить и не активны во время создания. - person Daniel Marbach; 18.01.2013