Автор: Sergey Teplyakov
Сегодня мы переходим к самому простому паттерну передачи зависимостей передаче через аргументы метода, Method Injection.
Существует две разновидности паттерна под названием Method Injection. В некоторых случаях под этим паттерном понимается установка зависимостей объекта с помощью вызова метода:
public interface IDependency {} public class CustomService { private IDependency _dependency, public void SetDependency(IDependency dependency) { _dependency = dependency, } }По сути, этот паттерн аналогичен рассмотренному ранее паттерну Property Injection с закрытом геттером, со всеми преимуществами и недостатками. Он применяется в языках без встроенной поддержки свойств, а также может успешно применяться в языке C#, если вам это больше нравится.
Вторым типом паттерна Method Injection является передача зависимости в метод, который будет использовать ее для решения текущей задачи, а не сохраняться во внутреннем поле для последующего использования. Именно этот вид паттерна мы сегодня и рассмотрим более подробно.
Описание
Суть паттерна Method Injection заключается в передаче зависимости определенному методу, необходимой для его успешной работы.
Назначение
Предоставить классу сервиса дополнительную информацию для выполнения определенной задачи.
Применимость
Зависимости, передаваемые через конструктор или свойство являются статическими зависимостями и требуются объекту на протяжении всего времени его жизни, и не изменяются от одной операции к другой. Однако бывают случаи, когда зависимость (ее реальный тип или состояние) может быть разной от вызова к вызову или это единственный способ передачи зависимости, поскольку метод является статическим.
Существует несколько случаев, когда более подходящим является передача зависимости именно через метод, а не через конструктор или свойство.
1. Метод является статическим и другие варианты не подходят.
В этом же контексте используется IFormatProvider в методе double.Parse и других аналогичных методах. Иногда этот подход применим и для бизнес объектов и может использоваться, например, для статической фабрики или других подобных целей.
2. Зависимость может изменяться от операции к операции.
Существует вариант паттерна Стратегия, при котором эта стратегия не может быть передана в аргументах конструктора, поскольку она требуется лишь одному методу и может изменяться от вызова к вызову. Классическим примером такой стратегии может служить стратегия сортировки, передаваемая методу List<,T>,.Sort().
Этот же подход может применяться и тогда, когда некоторая стратегия доступна в месте вызова операции, а не в месте создания объекта.
3. Передача локального контекста для выполнения операции.
Ряд паттернов проектирования, таких как Команда, Состояние и некоторые другие могут использовать дополнительный внешний контекст для выполнения операции. Этот же подход интенсивно используется в многопоточном программировании, когда в поток (или таймер) передается дополнительный контекст, известный вызывающему коду.
Известные применения
В составе .NET Framework этот подход используется достаточно интенсивно, как в контексте локальных стратегий, так и в контексте передачи контекста исполнения.
Локальные стратегии
Команды в WPF
Дополнительный контекст в многопоточности
Все привыкли, что самый простой способ добраться до внешнего контекста заключается в захвате внешних переменных. Тем не менее, есть и олдскульный подход, который заключается в явном протаскивании этого контекста через метод:// Использование контекста для передачи данных в другой поток. var context = new CustomViewModel(), var thread = new Thread(o =>, { var localContext = (CustomViewModel) o, }), thread.Start(context),
Ограничения
Главная проблема этого паттерна заключается в том, что ни один из существующих DI-контейнеров никак не сможет помочь в автоматизации получения и внедрения зависимости в метод. Поскольку вызов метода происходит явно, то вызывающему коду придется самостоятельно решить, откуда взять ему экземпляр зависимости создать экземпляр конкретного класса или переложить эту ответственность на свой верхний уровень.
Кроме того, к этому паттерну нужно отнестись с осторожностью, если он применяется потому, что некоторая зависимость нужна лишь одному методу в классе и нам лень из-за этого протаскивать ее через конструктор. Иногда этот подход оправдан, но он также может означать низкую внутреннюю связность (low cohesion) класса и нарушение Принципа Единой Обязанности: точно ли все нормально, что некоторая зависимость нужна лишь одной операции и не нужна другим методам? Может здесь скрыто два класса?
В отличие от Constructor Injection и Property Injection данный паттерн носит более локальный характер и не является типовым паттерном управления зависимостями в приложении. Тем не менее, если под зависимостями понимать локальный контекст или локальную стратегию, то этот паттерн вполне применим на практике.
Заключение
Method Injection сложно назвать очень уж распространенным паттерном в контексте управления зависимостями, тем не менее, это вполне распространенный подход в библиотеках, а также некоторых паттернах проектирования для протаскивания в операцию дополнительного контекста или стратегии, изменяемой от операции к операции.