Как можно внедрить зависимости в класс, если они различны для каждой операции?
Путем передачи в качестве параметра метода. Если при каждом вызове метода используется другая зависимость, вы можете передать ее через параметр метода.
Как это работает
Вызывающая сторона передает зависимость как параметр метода при каждом его вызове. Эта процедура не сложнее, чем сигнатура представленного ниже метода:
1 | public void DoStuff(ISomeInterface dependency) |
Часто зависимость будет представлять некоторый вид контекста для операции, передаваемого как соответствующее значение:
1 | public string DoStuff(SomeValue value, ISomeContext context). |
Если сервис использует зависимость, в нем прежде всего должна выполняться проверка на null.
Когда следует использовать внедрение метода
Внедрение метода лучше всего использовать, когда при каждом вызове методу задается другая зависимость. Это может происходить в случае, когда зависимость сама по себе представляет некоторое значение.
Существует несколько случаев, когда более подходящим является передача зависимости именно через метод, а не через конструктор или свойство:
- Метод является статическим и другие варианты не подходят. В этом же контексте используется IFormatProvider в методе double.Parse и других аналогичных методах.
- Зависимость может изменяться от операции к операции. Существует вариант паттерна «Стратегия«, при котором эта стратегия не может быть передана в аргументах конструктора, поскольку она требуется лишь одному методу и может изменяться от вызова к вызову. Классическим примером такой стратегии может служить стратегия сортировки, передаваемая методу List<T>.Sort(). Этот же подход может применяться и тогда, когда некоторая стратегия доступна в месте вызова операции, а не в месте создания объекта.
- Передача локального контекста для выполнения операции. Ряд паттернов проектирования, таких как Команда, Состояние и некоторые другие могут использовать дополнительный внешний контекст для выполнения операции. Этот же подход интенсивно используется в многопоточном программировании, когда в поток (или таймер) передается дополнительный контекст, известный вызывающему коду.
Примеры использования
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public interface ICommandContext { int ProcessorCount { get; } } // CustomCommand public void Execute(ICommandContext context) {} Локальные стратегии IFormatProvider provider = new NumberFormatInfo { NumberDecimalSeparator = ";" }; // Задаем "стратегию" разбора double var value = double.Parse("1;1", provider); IComparer<int> comparer = Comparer<int>.Default; var list = new List<int> {3, 4, 1}; // Передаем "стратегию" сортировки list.Sort(comparer); var task = Task.Run(() => { }); TaskScheduler taskScheduler = TaskScheduler.Current; // Задаем "стратегию" запуска "продолжения" задачи task.ContinueWith(t => { }, taskScheduler); |
Заключение
Внедрение через метод (Method Injection) сложно назвать очень уж распространенным паттерном в контексте управления зависимостями, тем не менее, это вполне распространенный подход в библиотеках, а также некоторых паттернах проектирования для протаскивания в операцию дополнительного контекста или стратегии, изменяемой от операции к операции.