Вопрос или проблема
Я хотел бы знать, могу ли я улучшить следующую реализацию, используя Kotlin и Guice
У меня есть базовый интерфейс
interface Event {
val eventInfo
val eventDate
}
Теперь у меня есть несколько реализаций этого события, и я использую вспомогательную инъекцию для их создания во время выполнения
class EventTypeA @Inject constructor(
@Assisted eventInfo,
@Assisted eventDate,
@Injected dependency1
): Event {}
class EventTypeB @Inject constructor(
@Assisted eventInfo,
@Assisted eventDate,
@Injected dependency2
): Event {}
Фабрика guice в данный момент выглядит следующим образом
interface EventFactory {
createEventTypeA(@Assisted eventInfo, @Assisted eventDate): EventTypeA
createEventTypeB(@Assisted eventInfo, @Assisted eventDate): EventTypeB
}
...
configure() {
install(FactoryModuleBuilder().build(EventFactory::class.java))
}
Это работает, я могу создать оба типа событий, просто передав ожидаемые вспомогательные значения. Теперь, что мне не нравится, так это то, что я по сути повторяю сигнатуры фабрики снова и снова. Если я реализую 5 различных типов событий, я в конечном итоге получу фабрику, как показано ниже
interface EventFactory {
createEventTypeA(@Assisted eventInfo, @Assisted eventDate): EventTypeA
createEventTypeB(@Assisted eventInfo, @Assisted eventDate): EventTypeB
createEventTypeC(@Assisted eventInfo, @Assisted eventDate): EventTypeC
createEventTypeD(@Assisted eventInfo, @Assisted eventDate): EventTypeD
createEventTypeE(@Assisted eventInfo, @Assisted eventDate): EventTypeE
}
Есть ли способ сделать это обобщенным образом? В идеале, я хотел бы, чтобы фабрика использовала общий тип или реализованный, в то время как инъектирует разные зависимости. Например:
val eventTypeA = eventFactory<EventTypeA>.create(eventInfo, eventDate)
Кажется, это было бы гораздо чище, оставив инъекцию зависимостей Guice. Я пытался найти решение, но не видел этого точно такого же примера в аналогичных постах
Ответ или решение
Для решения задачи с использованием Guice Factory в Kotlin для создания различных реализаций интерфейса Event
с разными зависимостями, вы можете сделать рефакторинг вашего текущего решения на более обобщённое. Это избавит вас от необходимости дублировать код в фабрике, занимаясь созданием объектов. Рассмотрим, как это можно реализовать.
Определение базового интерфейса
Сначала определим базовый интерфейс Event
:
interface Event {
val eventInfo: String
val eventDate: String
}
Реализация различных типов событий
Теперь создадим различные реализации этого интерфейса, используя Assisted Injection. Мы добавляем зависимости в конструкторы:
class EventTypeA @Inject constructor(
@Assisted override val eventInfo: String,
@Assisted override val eventDate: String,
@Inject private val dependency1: Dependency1
) : Event
class EventTypeB @Inject constructor(
@Assisted override val eventInfo: String,
@Assisted override val eventDate: String,
@Inject private val dependency2: Dependency2
) : Event
В этом примере Dependency1
и Dependency2
– это ваши зависимости, которые будут инъектироваться через Guice.
Обобщённая фабрика
Теперь создадим универсальную фабрику с использованием дженериков. Сначала создайте интерфейс фабрики:
interface EventFactory {
<T : Event> create(eventClass: Class<T>, eventInfo: String, eventDate: String): T
}
Реализация метода создания событий
Для реализации метода создания событий воспользуемся механизмом Guice, чтобы динамически создавать нужный тип:
class EventFactoryImpl @Inject constructor(
private val injector: Injector
) : EventFactory {
override fun <T : Event> create(eventClass: Class<T>, eventInfo: String, eventDate: String): T {
return injector.getProvider(eventClass).get().also {
(it as? Event)?.apply {
// Здесь можно добавить дополнительную логику при необходимости
}
}
}
}
Регистрация фабрики в модуле Guice
Теперь зарегистрируйте ваш EventFactory в модуле Guice:
class EventModule : AbstractModule() {
override fun configure() {
bind(EventFactory::class.java).to(EventFactoryImpl::class.java)
}
}
Использование фабрики
Теперь вы можете использовать вашу фабрику для создания экземпляров различных событий, которые будут иметь свои уникальные зависимости:
@Inject
private lateinit var eventFactory: EventFactory
fun createEvents() {
val eventTypeA: EventTypeA = eventFactory.create(EventTypeA::class.java, "Info A", "Date A")
val eventTypeB: EventTypeB = eventFactory.create(EventTypeB::class.java, "Info B", "Date B")
}
Заключение
С помощью этого подхода вы сможете избежать дублирования кодов в вашем EventFactory
. Вместо этого, используя дженерики, вы упростите процесс создания объектов и добавите гибкость в архитектуру вашего приложения. Данный подход улучшает поддержку и расширяемость кода, позволяя вам легко добавлять новые реализации интерфейса Event
без необходимости изменять интерфейс фабрики. Это делает ваш код более чистым и управляемым.