Инъекция зависимостей через конструкторы или средства определения свойств?

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

Это не похоже на шаблон с существующим кодом здесь, поэтому я ищу, чтобы узнать, каков общий консенсус, плюсы и минусы конструкторов по сравнению с свойствами. Лучше ли использовать средства настройки свойств?

Ответ 1

Ну, это зависит: -).

Если класс не может выполнять свою работу без зависимости, добавьте его в конструктор. Класс нуждается в новой зависимости, поэтому вы хотите, чтобы ваши изменения нарушали ситуацию. Кроме того, создание класса, который не полностью инициализирован ( "двухступенчатая конструкция" ), является анти-шаблоном (IMHO).

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

Ответ 2

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

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

public class ClassExample
{
    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo)
        : this (dependnecyOne, dependencyTwo, new DependnecyThreeConcreteImpl())
    { }

    public ClassExample(IDependencyOne dependencyOne, IDependencyTwo dependencyTwo, IDependencyThree dependencyThree)
    {
        // Set the properties here.
    }
}

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

Ответ 3

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

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

Ответ 4

Если у вас есть большое количество дополнительных зависимостей (это уже запах), то, вероятно, установка инжектора - это путь. Инъекция конструктора лучше показывает ваши зависимости.

Ответ 5

Общий предпочтительный подход - максимально использовать инъекцию конструктора.

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

Попробуйте иметь только один конструктор, он упрощает проектирование и избегает двусмысленности (если не для людей, для контейнера DI).

Вы можете использовать инъекцию свойств, когда у вас есть то, что Mark Seemann называет локальным значением по умолчанию в своей книге "Инъекция зависимостей в .NET": зависимость не является обязательной, поскольку вы можете предоставить прекрасную рабочую реализацию, но хотите разрешить вызывающему пользователю указать при необходимости, другой.

(Бывший ответ ниже)


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

Инъекция сеттера хороша, если инъекция является необязательной, или если вы хотите изменить ее на полпути. Обычно мне не нравятся сеттеры, но это вопрос вкуса.

Ответ 6

Я лично предпочитаю шаблон "Извлечение и переопределение" для инъекций зависимостей в конструкторе, в основном по причине, изложенной в вашем вопросе. Вы можете установить свойства как virtual, а затем переопределить реализацию в производном тестируемом классе.

Ответ 7

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

Пока интерфейс классов (API) ясен в том, что ему нужно для выполнения своей задачи, ты хороший.

Ответ 8

Я предпочитаю впрыск конструктора, потому что он помогает "принудительно выполнять" требования к зависимостям класса. Если это в c'tor, потребитель должен установить объекты, чтобы приложение могло компилироваться. Если вы используете инъекцию setter, они могут не знать, что у них есть проблема до времени выполнения - и в зависимости от объекта может быть поздно во время выполнения.

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

Ответ 9

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

Я также использую инъекцию свойств для настройки того, что в контейнере нет ссылок на такие, как представление ASP.NET на презентаторе, созданном с использованием контейнера.

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

Ответ 10

Один из вариантов, который стоит рассмотреть, заключается в создании сложных множественных зависимостей из простых одиночных зависимостей. То есть определите дополнительные классы для составных зависимостей. Это немного упрощает инъекцию конструктора WRT - меньшее количество параметров для каждого вызова, при этом сохраняя при этом обязательную необходимость в поставке-все-зависимостях.

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

Лично, однако, я больше поклонник использования методов/свойств, которые задают зависимости, параметры и т.д. Имена вызовов помогают описать, что происходит. Это хорошая идея, например, предоставить примеры этого-то-как-установить-снизу, и убедиться, что зависимый класс делает достаточно проверок ошибок. Вы можете использовать модель конечного состояния для настройки.

Ответ 11

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

Использование необязательных параметров может быть выполнено только в рамках поддерживающих их программ, таких как .NET 4 (как для С#, так и для VB.NET, хотя у них всегда были VB.NET). Разумеется, вы можете выполнить аналогичную функциональность, просто используя свойство, которое может быть переназначено потребителем вашего класса, но вы не получаете преимущества неизменности, предоставляемой с помощью объекта частного интерфейса, назначенного параметру конструктора.

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

Ответ 12

Это старый пост, но если это необходимо в будущем, возможно, это будет полезно:

https://github.com/omegamit6zeichen/prinject

У меня была схожая идея, и я придумал эту структуру. Вероятно, это далеко не полный, но это идея рамки, фокусирующейся на введении свойств

Ответ 13

Это зависит от того, как вы хотите реализовать. Я предпочитаю инъекцию конструктора, где я чувствую, что значения, которые вступают в реализацию, не часто меняются. Например: если compnay stragtegy идет с сервером oracle, я сконфигурирую свои значения datsource для достижимых соединений bean через инъекцию конструктора. Кроме того, если мое приложение является продуктом и, возможно, оно может подключиться к любому db клиента, я бы реализовал такую ​​конфигурацию db и реализацию нескольких брендов посредством инсталляции setter. Я только что привел пример, но есть более эффективные способы реализации описанных выше сценариев.

Ответ 14

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

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