DI и рантайм переменные
Допустим, есть класс, пусть это будет какой-то клиент для наглядности. В него нужно передать сервисы, с этим проблем нету, конструктор DI, сервисы всегда одинаковые, все хорошо. Но, в него так же надо передать baseUrl, secret и тд - это рантайм переменные, мы не можем знать их на момент компиляции.
Вариант без DI
public class Client
{
public Client(HttpClientFactory httpFactory, string baseUrl, string secret) {};
public void PostSmth();
}
Использование:
var client = new Client(new HttpClientFactory (), "stack.com", "zog");
client.PostSmth();
Как быть с DI (коробочный .net core)? Варианты:
Передавать рантайм переменные в методы, а не в конструктор:
var client = new Client(new HttpClientFactory ()); client.PostSmth("stack.com", "zog");
Проблема: лишние перемнные во всех публичных методах, для одного инстанса они, в принипе, будут одни и теже.
- Создать Init метод
public class Client
{
public Client(HttpClientFactory httpFactory) {};
public void PostSmth();
public void Init(string baseUrl, string secret) {this.base = baseUrl... }
}
Проблема: инстанс обьекта не есть рабочим без вызова Init.
- Создать фабрику и спрятать Init там. Проблема: доп сущность "фабрика", свои проблемы с реализацией (я пришел к тому, что использую сервис локатор внутри фарик, но это уже отдельная история)
Option паттерн не подходит, так как это не конфиги, а именно рантайм переменные. Есть другие варианты? Как быть?
UPD
- И последний вариант - не использовать DI для таких классов, а создавать инстансы вручную.
UPD 2
По комментам можно сделать вывод, что фабрики - хорошо. Какие фабрики тогда использовать? По примеру с комментов factory class использует сервис локатор. Так все таки, допустимо ли это? Является ли factory class copposition root-ом? По определению отсюда - нет, ибо это отдельный класс и будет жить далеко от стартапа, например. Так что же остается, только factory method? Или factory class тоже можно использовать?
Ответы (1 шт):
На мой субъективный взгляд первый вариант является самым честным. Потому что он дествительно отображает реальный интерфейс нашей операции и динамичность параметров. Поясню ниже с примерами требований чтобы показать различия.
- выкачивать картинку с любого сайта с которого укажет пользователь;
- выкачивать любую картинку с сайта который указан в конфиге;
- выкачивать любую картинку с сайта который указан в конфиге и иметь возможность динамически менять урл сайта в конфиге;
Для этих трех случаях я вижу два варианта реализации. Первый он динамичен и тут передача в метод на мой взгляд правильное решение. Вторые два решаются с помощью инжекции в конструктор.
Я иногда смотрю на это с такой перспективы. Partial application в ФП мире это тоже самое что внедрение зависимостей в объектом мире. То есть если нам надо запомнить аргументы и дальше использовать объект в нескольких местах с этими же аргументами значит они подлежат инжекции в конструктор например и в момент резолва контролера можно вычитывать их из конфига, даже если сам конфиг может динамически меняться, только меняется время жизни этого класса. Другой вариант когда например вам пришел на вход массив урлов откуда надо выкачать картинки это аргумент операции и инжектировать в конструктор не особо полезно так как реализация такого пользовательского сценария усложнится. В первую очередь надо смотреть на требования и как они будут реализованы в коде, а не пытаться свести все к ижектированию. А если очень хочется то у Марка Симана в книге есть глава с примером внедрение в метод. Мне кажется это очень похоже.
Последнее что хочется добавить, я бы учел то как много будет создаваться Client-ов и как возможно обслуживать максимальное количество операции с минимальным количеством HttpClient.