Kotlin защитная оболочка - PullRequest
0 голосов
/ 20 апреля 2020

Я ищу элегантное решение для следующего.

Я хотел бы реализовать класс Wrapper, который:

  • Принимает 2 реализации одного и того же Interface и возвращает новый экземпляр того же Interface.
  • Любой вызов метода для объекта Wrapper пытается вызвать тот же метод в 1-й реализации.
  • Если первый вызов результаты в UnsupportedOperationException, тогда вместо этого следует использовать 2-ю реализацию.
interface API {
    fun getData(): String
}

class Main: API {
    override fun getData(): String {
        throw UnsupportedOperationException()
    }
}

class Fallback: API {
    override fun getData(): String {
        return "data"
    }
}

class Wrapper {
    companion object {
        fun getInstance(main: API, fallback: API): API {
            // TODO
        }
    }
}

class Test {
    @Test
    fun `invokes the fallback instance`() {
        val wrapper = Wrapper.getInstance(Main(), Fallback())
        val response = wrapper.getData()
        assertEquals(response, "data")
    }
}

Лучшее, что я до сих пор придумал, - это делегирование с переопределениями:

class Wrapper(fallback: API): API by Main() {
    val fallback = fallback
    override fun getData(): String {
        return fallback.getData()
    }
}

Что мне не нравится в этом решении, так это:

  • Это требует переопределения каждой неподдерживаемой операции
  • Это становится довольно многословным, когда Интерфейс превращается в сложную многоуровневую структуру с большим количеством суб интерфейсы

Я также хотел бы избежать Reflection по соображениям производительности и потому, что это Kotlin Мультиплатформенный проект.

Любые предложения приветствуются.

Спасибо Хуан

1 Ответ

0 голосов
/ 20 апреля 2020

Предложенное решение не будет работать, поскольку оно всегда будет использовать запасной вариант для любой переопределенной функции.

Нет решения для ваших нужд, которое может избежать необходимости вручную обрабатывать каждую функцию вашего интерфейса. Но у вас может быть промежуточная функция, которая обрабатывает каскадный выбор реализации для функций с одинаковой сигнатурой.

class Wrapper (private val delegates: Array<out API>): API {

    companion object {
        fun getInstance(vararg delegates: API) = Wrapper(delegates)
    }

    private fun <R> delegate0Arg(function: API.() -> R): R {
        for (delegate in delegates) {
            try {
                return delegate.function()
            } catch (e: UnsupportedOperationException) {
                // continue
            }
        }
        throw UnsupportedOperationException()
    }

    override val name: String get() = delegate0Arg(API::name)
    override fun getData(): String = delegate0Arg(API::getData)

}

Но вам потребуются дополнительные функции для обработки каждого уникального числа аргументов, которые имеют функции интерфейса.

private fun <T, R> delegate1Arg(t: T, function: API.(t: T) -> R): R {
    for (delegate in delegates) {
        try {
            return delegate.function(t)
        } catch (e: UnsupportedOperationException) {
            // continue
        }
    }
    throw UnsupportedOperationException()
}

override fun getData(x: String) = delegate1Arg(x, API::getData)
...