Я новичок в внедрении зависимостей, и у меня есть несколько вопросов о том, какой стиль я должен использовать в своих приложениях. Я только что прочитал статью Мартин Фаулер « Инверсия контейнеров управления» и «Шаблон внедрения зависимостей », но не могу понять практической разницы между инжектором конструктора, сеттера и интерфейса.
Мне кажется, что причины использования одного над другим - просто вопрос очистки кода и / или ясности. В чем разница? Есть ли какие-либо сильные преимущества или недостатки использования одного над другим, или это только то, что я уже говорил ранее?
На мой взгляд, внедрение конструктора является наиболее интуитивным, а внедрение интерфейса - наименьшим. С другой стороны, внедрение метода является средним термином, но должны ли вы изменять экземпляр объекта зависимости, который вы изначально внедрили? Гарантирует ли этот стиль внедрения, что объект, который нуждается в зависимости, всегда будет внедряться? Я не верю, но, пожалуйста, поправьте меня, если я ошибаюсь.
Ответы:
Преимущество Constructor Injection заключается в том, что оно делает зависимость явной и вынуждает клиента предоставлять экземпляр. Это также может гарантировать, что клиент не сможет изменить экземпляр позже. Одним (возможным) недостатком является то, что вам нужно добавить параметр в ваш конструктор.
Преимущество Setter Injection заключается в том, что он не требует добавления параметра в конструктор. Это также не требует, чтобы клиент установил экземпляр. Это полезно для необязательных зависимостей. Это также может быть полезно, если вы хотите, чтобы класс создавал, например, реальное хранилище данных по умолчанию, а затем в тесте вы можете использовать установщик, чтобы заменить его экземпляром тестирования.
Интерфейсная инъекция , насколько я могу судить, не сильно отличается от настройки инъекции. В обоих случаях вы (необязательно) устанавливаете зависимость, которую можно изменить позже.
В конечном счете, это вопрос предпочтения и того, требуется ли зависимость . Лично я использую конструктор инъекций почти исключительно. Мне нравится, что он делает зависимости класса явными, заставляя клиента предоставлять экземпляр в конструкторе. Мне также нравится, что клиент не может изменить экземпляр после факта.
Часто моя единственная причина перехода на две отдельные реализации - это тестирование. В производстве я могу перейти в a
DataRepository
, но в тестировании я бы прошел вFakeDataRepository
. В этом случае я обычно предоставляю два конструктора: один без параметров, а другой принимает aIDataRepository
. Затем в конструкторе без параметров я буду связывать вызов второго конструктора и передавать вnew DataRepository()
.Вот пример в C #:
Это известно как инъекция зависимости бедного человека. Мне это нравится, потому что в производственном клиентском коде мне не нужно повторяться, имея несколько повторяющихся утверждений, которые выглядят как
Тем не менее, я все еще могу передать альтернативную реализацию для тестирования. Я понимаю, что с DI Бедного Человека я жестко программирую свою зависимость, но это приемлемо для меня, так как я в основном использую DI для тестированияисточник
Различия между внедрением в конструктор и сеттер уже достаточно описаны выше, поэтому я не буду более подробно останавливаться на них.
Внедрение интерфейса - это более продвинутая форма внедрения, которая полезна, поскольку позволяет определять зависимость в момент ее использования, а не во время инициализации объекта, который будет ее использовать. Это позволяет использовать несколько полезных опций:
Зависимость может быть по-разному ограничена объектом, в который она вводится; Например, вы можете использовать внедрение интерфейса для предоставления объекта, для которого он существует, для сеанса пользователя или для потока в глобальном синглтоне. Каждый раз, когда объект нуждается в зависимости, он будет вызывать метод getter, предоставляемый платформой, и это может возвращать разные результаты в зависимости от ситуации, в которой он вызывается.
Это допускает ленивую инициализацию - нет необходимости инициализировать зависимость, пока она не собирается использоваться
Это позволяет загружать зависимости из кэшированной копии, когда они существуют, или переинициализировать, когда их нет (например, с помощью
SoftReference
в Java).Очевидно, что передовые методы, подобные этому, имеют свои недостатки; в этом случае основная проблема состоит в том, что код становится менее понятным (классы, используемые в вашем коде, становятся абстрактными, и нет очевидной конкретной реализации их, что может сбить с толку, если вы к этому не привыкли), и вы становитесь более зависимыми в вашей структуре внедрения зависимостей ( конечно, все еще возможно создавать экземпляры ваших объектов вручную, но это сложнее, чем с другими стилями внедрения).
источник