Я использовал различные контейнеры IoC (Castle.Windsor, Autofac, MEF и т. Д.) Для .Net в ряде проектов. Я обнаружил, что они часто подвергаются насилию и поощряют ряд плохих практик.
Существуют ли какие-либо устоявшиеся практики использования контейнера IoC, особенно при предоставлении платформы / инфраструктуры? Моя цель как разработчика фреймворка - сделать код максимально простым и легким в использовании. Я бы предпочел написать одну строку кода для создания объекта, чем десять или даже две.
Например, несколько запахов кода, которые я заметил, и у которых нет хороших предложений:
Большое количество параметров (> 5) для конструкторов. Создание услуг имеет тенденцию быть сложным; все зависимости внедряются через конструктор - несмотря на то, что компоненты редко бывают необязательными (за исключением, может быть, при тестировании).
Отсутствие частных и внутренних занятий; это может быть конкретным ограничением использования C # и Silverlight, но мне интересно, как это решить. Трудно сказать, что такое интерфейс фреймворков, если все классы общедоступны; это позволяет мне получить доступ к частным частям, которые я, вероятно, не должен касаться.
Связь жизненного цикла объекта с контейнером IoC. Часто трудно вручную построить зависимости, необходимые для создания объектов. Жизненный цикл объекта слишком часто управляется платформой IoC. Я видел проекты, где большинство классов зарегистрированы как синглтоны. Вы получаете отсутствие явного контроля, а также вынуждены управлять внутренними компонентами (это относится к вышеприведенному пункту, все классы общедоступны, и вы должны вводить их).
Например, .Net Framework имеет много статических методов. например, DateTime.UtcNow. Много раз я видел это завернутым и введенным в качестве параметра конструкции.
В зависимости от конкретной реализации, мой код сложно тестировать. Внедрение зависимости затрудняет использование моего кода - особенно если в классе много параметров.
Как предоставить как тестируемый интерфейс, так и простой в использовании? Каковы лучшие практики?
Ответы:
Единственный законный анти-шаблон внедрения зависимостей, о котором я знаю, - это шаблон Service Locator , который является анти-шаблоном, когда для него используется структура DI.
Все другие так называемые анти-паттерны DI, о которых я слышал, здесь или в другом месте, являются лишь немного более конкретными случаями общих анти-паттернов разработки ОО / программного обеспечения. Например:
Чрезмерная инъекция в конструктор является нарушением принципа единой ответственности . Слишком много аргументов конструктора указывает на слишком много зависимостей; слишком много зависимостей указывает на то, что класс пытается сделать слишком много. Обычно эта ошибка коррелирует с другими запахами кода, такими как необычно длинные или неоднозначные («менеджер») имена классов. Инструменты статического анализа могут легко обнаружить чрезмерную афферентную / эфферентную связь.
Внедрение данных, в отличие от поведения, является подтипом анти-паттерна полтергейста , в данном случае «геист» является контейнером. Если класс должен знать текущую дату и время, вы не вводите a
DateTime
, который является данными; вместо этого вы вводите абстракцию через системные часы (я обычно называю моиISystemClock
, хотя я думаю, что есть более общая в проекте SystemWrappers ). Это не только правильно для DI; это абсолютно необходимо для тестируемости, так что вы можете тестировать изменяющиеся во времени функции без необходимости их фактического ожидания.Объявление каждого жизненного цикла Синглтоном является для меня отличным примером программирования культа грузов и, в меньшей степени, разговорно названной « выгребной ямы объектов ». Я видел больше злоупотреблений синглтоном, чем хотел бы вспомнить, и очень мало из них связано с DI.
Другой распространенной ошибкой являются специфические для реализации типы интерфейсов (со странными именами, например
IOracleRepository
), которые делаются только для того, чтобы иметь возможность зарегистрировать его в контейнере. Это само по себе является нарушением принципа инверсии зависимостей (просто потому, что это интерфейс, не означает, что он действительно абстрактный) и часто также включает в себя раздувание интерфейса, которое нарушает принцип сегрегации интерфейса .Последняя ошибка, которую я обычно вижу, это «необязательная зависимость», которую они сделали в NerdDinner . Другими словами, есть конструктор , который принимает инъекции зависимостей, но и другой конструктор , который использует реализацию « по умолчанию». Это также нарушает DIP и имеет тенденцию приводить к LSP нарушений , а также, как разработчики, в течение долгого времени, начинают делать предположения вокруг реализации по умолчанию, и / или начать новые-Инг до экземпляров с помощью конструктора по умолчанию.
Как гласит старая поговорка, вы можете писать на Фортране на любом языке . Dependency Injection не серебряная пуля , которая будет препятствовать разработчикам завинчивания их управления зависимостями, но это действительно предотвратить ряд распространенных ошибок / анти-шаблоны:
...и так далее.
Очевидно, что вы не хотите разрабатывать структуру, зависящую от конкретной реализации контейнера IoC , такой как Unity или AutoFac. То есть, еще раз, нарушая DIP. Но если вы даже задумываетесь над тем, чтобы сделать что-то подобное, то вы, должно быть, уже допустили несколько ошибок проектирования, потому что Dependency Injection - это универсальный метод управления зависимостями, который не связан с концепцией контейнера IoC.
Все может построить дерево зависимостей; может быть, это контейнер IoC, может быть, это юнит-тест с кучей насмешек, может быть, это тестовый драйвер, предоставляющий фиктивные данные. Ваша инфраструктура не должна заботиться, и большинство платформ, которые я видел, не заботятся, но они все еще интенсивно используют внедрение зависимостей, так что она может быть легко интегрирована в выбранный контейнер IoC конечного пользователя.
Я не ракетостроение. Просто старайтесь избегать
new
иstatic
кроме случаев , когда есть веские причины , чтобы использовать их, например, метод полезности , который не имеет внешних зависимостей, или служебный класс , который не мог бы иметь какой - либо цели вне рамок (Interop оберток и ключи словаря являются общими примерами это).Многие проблемы со средами IoC возникают, когда разработчики впервые изучают, как их использовать, и вместо того, чтобы фактически изменить способ обработки зависимостей и абстракций в соответствии с моделью IoC, вместо этого попробуйте манипулировать контейнером IoC, чтобы удовлетворить ожидания своих старый стиль кодирования, который часто подразумевал бы высокую связь и низкую когезию. Плохой код - это плохой код, использует ли он методы DI или нет.
источник