Используйте фабрику Guice для создания нескольких реализаций интерфейса, но с разными зависимостями.

Вопрос или проблема

Я хотел бы знать, могу ли я улучшить следующую реализацию, используя 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 без необходимости изменять интерфейс фабрики. Это делает ваш код более чистым и управляемым.

Оцените материал
Добавить комментарий

Капча загружается...