Внедрение зависимостей через конструкторы или установщики свойств?

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

$("#foo.bar"); // Matches 

Это должно выглядеть так, как будто вы пишете в CSS. Обратите внимание, что это не относится ко всем #foo элементам (хотя должно быть только одно), и оно не будет применяться ко всем .bar элементам (хотя их может быть много). Он будет ссылаться только на элементы, которые подходят для обоих атрибутов.

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

$(".bar:first").is("#foo"); // TRUE if first '.bar' in document is also '#foo'

144
задан kiamlaluno 26 June 2017 в 23:36
поделиться

11 ответов

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

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

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

124
ответ дан 23 November 2019 в 22:49
поделиться

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

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

17
ответ дан 23 November 2019 в 22:49
поделиться

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

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

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

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

20
ответ дан 23 November 2019 в 22:49
поделиться

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

11
ответ дан 23 November 2019 в 22:49
поделиться

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

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

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

6
ответ дан 23 November 2019 в 22:49
поделиться

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

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

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

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

(Предыдущий ответ ниже)


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

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

11
ответ дан 23 November 2019 в 22:49
поделиться

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

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

7
ответ дан 23 November 2019 в 22:49
поделиться

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

7
ответ дан 23 November 2019 в 22:49
поделиться

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

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

6
ответ дан 23 November 2019 в 22:49
поделиться

One option that might be worth considering is composing complex multiple-dependencies out of simple single dependencies. That is, define extra classes for compound dependencies. This makes things a little easier WRT constructor injection - fewer parameters per call - while still maintaining the must-supply-all-dependencies-to-instantiate thing.

Of course it makes most sense if there's some kind of logical grouping of dependencies, so the compound is more than an arbitrary aggregate, and it makes most sense if there are multiple dependents for a single compound dependency - but the parameter block "pattern" has been around for a long time, and most of those that I've seen have been pretty arbitrary.

Personally, though, I'm more a fan of using methods/property-setters to specify dependencies, options etc. The call names help describe what is going on. It's a good idea to provide example this-is-how-to-set-it-up snippets, though, and make sure the dependent class does enough error checks. You might want to use a finite state model for the setup.

3
ответ дан 23 November 2019 в 22:49
поделиться

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

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

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

3
ответ дан 23 November 2019 в 22:49
поделиться