Рассмотрим четыре шаблона для внедрения зависимости в объект.
1. Constructor Injection (внедрение через конструктор)
Constructor Injection это способ внедрения зависимостей, когда необходимые объекты запрашиваются классом как параметры его конструктора.
Принцип реализации
- Создаем в классе private readonly поле, для хранения ссылки на экземпляр объекта. Указание readonly предохраняет его от случайного изменения в дальнейшем.
- Запрашиваем объект как параметр конструктора.
- Проверяем, что полученное значение не является пустой ссылкой (null).
- Сохраняем в созданном выше поле для дальнейшего использования.
Плюсы
- Очень простой для понимания и использования способ.
Минусы
- В момент создания объекта придется породить экземпляры всех классов, от которых он зависит;
- При наличии нескольких конструкторов необходимо описать логику выбора нужного варианта;
- Возможно потребуется модификация существующего кода для поддержки конструкторов с параметрами.
2. Property Injection (внедрение через свойство)
При использовании Property Injection объект предоставляет свойства, в которые могут быть переданы экземпляры классов от которых он зависит.
Обратите внимание, данная схема внедрения не носит обязательного характера. Например, разработчик может просто забыть передать зависимость. Для решения данной проблемы может быть использована реализация по умолчанию. Такой подход схож с шаблоном проектирования Стратегия.
Принцип реализации
- Создаем открытое для записи свойство, в котором будет размещен экземпляр требуемого объекта.
- Хорошей практикой считается создание реализации по умолчанию. Её экземпляр должен быть порожден при первом обращении к свойству, если зависимости не была передана до этого.
Плюсы
- Способ прост в использовании.
- Применим в случае, когда зависимость не может быть передана в момент создания класса.
Минусы
- Нет гарантии что зависимость будет внедрена в объект.
- Необходимость четко определить что будет, если клиентский код попытается передать новый экземпляр или пустую ссылку (null) в процессе работы приложения.
3. Method Injection (внедрение через параметр метода)
Method Injection подразумевает внедрение зависимости через параметр метода. Стоит отметить, что при каждом обращении может передаваться другой объект, в зависимости от текущего состояния приложения.
Принцип реализации
- Один из параметров метода используется для передачи зависимости.
- При обращении к методу проверяем, что полученное значение не является пустой ссылкой (null).
- Используем его в процессе выполнения метода.
- Область использования полученного объекта ограничивается данным методом.
Плюсы
- Позволяет передавать зависимость, определяемую текущем контекстом выполнения.
Минусы
- Ограниченная область применения.
4. Ambient Context (фоновый контекст)
Идея Ambient Context заключается в создании pubic static свойства или метода, предоставляющих экземпляр объекта определенного типа и доступного любому объекту в приложении. По смыслу такой подход применим для зависимостей, предоставляемых framework и прочими глобальными объектами приложения.
Примеры шаблона в .NET: Application.Current, Thread.CurrentCultureand, Thread.CurrentUICulture, System.Web.Http.GlobalConfiguration.Configuration, Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver, OperationContext.Current и т.п.
Необходимо обратить внимание, что Ambient Context, в отличии от шаблона Service Locator, предоставляет объект одного, заранее известного типа. Это напоминает шаблон Singleton.
Принцип реализации
- Создаем pubic static свойство или метод, возвращающие объект заданного типа.
- Порождается экземпляр по умолчанию, даже если клиентский код может установить свой объект. Ambient Context должен быть всегда доступен.
Плюсы
- Всегда доступен.
- Хорошо подходит в ситуации, когда определенная зависимость необходима многим объектам приложения и эти объекты заранее неизвестны.
Минусы
- Неявная зависимость. Чрезмерное увлечение таким подходом может сильно запутать код.
- Сложность в реализации, с учетом многопоточности и других особенностей приложения.