На данный момент я выбрал первый вариант, но мне не очень удобно с ним, я считаю, что должен быть лучший способ сделать это.
Многое из время, первый вариант в порядке - так что вы должны практиковаться с ним комфортно. В основном это означает больше думать о том, для чего нужна инъекция зависимости, и иметь четкое представление о том, действуют ли здесь эти силы.
Если Баннер является сущностью, в смысле domain-driven-design
, тогда это, вероятно, нечто аналогичное автомату в памяти. У него есть структура данных, которой он управляет, и некоторые функции для изменения этой структуры данных или ответов на интересные вопросы об этой структуре данных, но у него нет проблем ввода-вывода, базы данных, сети и т. Д. c.
Это, в свою очередь, говорит о том, что вы можете запускать его одинаково во всех контекстах - вам не нужна куча замещающих реализаций, чтобы сделать его тестируемым. Вы просто создаете один экземпляр и вызываете его методы.
Если он работает одинаково во всех контекстах, тогда ему не нужно настраиваемое поведение. Если вам не нужно настраивать поведение, то вам не нужно внедрять зависимости (поскольку все копии этой сущности будут использовать (копии) одинаковые зависимости.
Когда у вас есть конфигурируемое поведение, тогда анализу нужно будет рассмотреть область действия. Если вам нужно изменить это поведение с одного вызова на другой, то вызывающая сторона должна знать об этом. Если поведение меняется реже чем это, тогда вы можете начать изучать, имеет ли смысл «внедрение в конструктор».
Вы знаете, что намереваетесь использовать один BannerReplacer для данного вызова метода, поэтому вы можете сразу начать с метода, который выглядит как :
class Banner {
void doTheThing(arg, bannerReplacer) {
/* do the bannerReplacer thing */
}
}
Обратите внимание, что эта подпись вообще не зависит от времени жизни bannerReplacer. В частности, BannerReplacer может иметь более длительное время жизни, чем Banner, или более короткое время жизни. Мы заботимся только о том, чтобы срок службы был больше, чем у метода doTheThing.
class Banner {
void doTheThing(arg) {
this.doTheThing(arg, new BannerReplacer())
}
// ...
}
Здесь вызывающей стороне вообще не нужно знать о BannerReplacer; мы будем использовать новую копию реализации по умолчанию каждый раз. Вызывающая сторона заботится о том, какая реализация используется самостоятельно.
class Banner {
bannerReplacer = new BannerReplacer()
void doTheThing(arg) {
this.doTheThing(arg, this.bannerReplacer)
}
// ...
}
Та же идея, что и раньше; мы просто используем экземпляр BannerReplacer с более длительным временем жизни.
class Banner {
Banner() {
this(new BannerReplacer())
}
Banner(bannerReplacer) {
this.bannerReplacer = bannerReplacer;
}
void doTheThing(arg) {
this.doTheThing(arg, this.bannerReplacer)
}
// ...
}
Та же идея, что и раньше, но теперь мы разрешаем «внедрение» реализации по умолчанию, которая может пережить данный экземпляр Banner .
В долгосрочной перспективе удобство анализа заключается в том, чтобы понять требования текущей проблемы и выбрать подходящий инструмент.