Внедрение зависимостей подвергает внутренние уровни бизнес-уровня уровню пользовательского интерфейса - конечно, плохо? - PullRequest
3 голосов
/ 14 сентября 2011

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

Однако из-за необходимостичтобы удовлетворить зависимости, контейнеру DI также необходимо знать об интерфейсах и классах, к которым иначе не требовался бы доступ.Например:

Уровень пользовательского интерфейса должен работать с интерфейсом IWidgetManager.Настроенная имплантация ConcreteWidgetManager.Это зарегистрировано в контейнере DI.

ConcreteWidgetManager зависит от IWidgetRepository.Желаемая реализация для этого - ConcreteWidgetRepository.Таким образом, контейнер DI также должен знать о IWidgetRepository и ConcreteWidgetRepository.

Если бы я просто жестко закодировал зависимость ConcreteWidgetManager от ConcreteWidgetRepository, тогда ConcreteWidgetRepository мог бы быть внутренним и, следовательно, невидимым дляСлой пользовательского интерфейса.Никакой код пользовательского интерфейса не может обойти уровень диспетчера и работать непосредственно со слоем репозитория.

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

Правильно ли я думаю об этом, и есть ли способ смягчить ситуацию?

Ответы [ 3 ]

2 голосов
/ 15 сентября 2011

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

Однако добавление дополнительных сборок имеет свою стоимость. Как на техническое обслуживание, так и на время, необходимое для компиляции вашего решения. Вместо применения архитектурных правил с использованием сборок, я бы применял их с четкими и простыми рекомендациями или, возможно, с помощью такого инструмента, как NDepend. Однако, когда группа достаточно мала, использование проверок кода будет достаточно эффективным, чтобы предотвратить разрушение архитектуры.

2 голосов
/ 14 сентября 2011

То, как вы описываете это, подразумевает, что уровень пользовательского интерфейса отвечает за создание контейнера DI.

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

Да, есть НЕКОТОРЫЙ код, который создает контейнер DI и слой UI в определенном порядке. Может случиться так, что уровень пользовательского интерфейса вызывает функцию CreateDIContainer, которая инициализирует все компоненты. Но эта единственная функция - единственный случай, когда упоминаются реализации; все другие аспекты уровня пользовательского интерфейса связаны с абстрактными интерфейсами. Пурист может быть проблемой, но на самом деле, в 99,5% кода, который НЕ CreateDIContainer, пользовательский интерфейс не знает, что такое реализации. Переместите функцию инициализации в отдельный статический класс в отдельной библиотеке, если это делает вас счастливее.

<ч /> Я также заинтригован тем, что ты говоришь

Если бы я просто жестко запрограммировал зависимость ConcreteWidgetManager от ConcreteWidgetRepository ....,

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

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

(См. Вам это не нужно .)

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

Да, это нарушает инкапсуляцию. Но это цена, которую вы должны заплатить за использование DI. Это делает ваш код более тестируемым, но да нарушает инкапсуляцию.

С точки зрения API класса ваш класс, который запрашивает внедрение зависимостей, становится настолько глупым, насколько это возможно, но класс, который вызывает этот класс, теперь знает слишком много. В твоем случае рамки. Если вы используете DI в целом в своем коде, например, с помощью инъекции в конструктор, то при вызове класса вам нужно знать о нескольких классах.

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