Внедрение зависимостей через конструкторы или установщики свойств? - PullRequest
143 голосов
/ 01 октября 2009

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

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

Ответы [ 14 ]

123 голосов
/ 01 октября 2009

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

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

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

19 голосов
/ 01 октября 2009

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

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

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.
    }
}

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

17 голосов
/ 01 октября 2009

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

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

11 голосов
/ 01 октября 2009

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

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

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

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

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


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

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

11 голосов
/ 01 октября 2009

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

7 голосов
/ 01 октября 2009

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

7 голосов
/ 01 октября 2009

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

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

6 голосов
/ 01 октября 2009

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

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

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

6 голосов
/ 01 октября 2009

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

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

3 голосов
/ 03 августа 2010

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

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

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

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