Site icon AppTractor

Разделение интерфейсов в Android с помощью расширений Kotlin и inline функций

Роберт Мартин, известный как «Дядя Боб», сформулировал «Принципы SOLID» — рекомендации, которым следует следовать при создании программного обеспечения, чтобы его было легче масштабировать и поддерживать. SOLID — это аббревиатура, и каждая буква обозначает принцип объектно-ориентированного проектирования. В этой статье мы сосредоточимся на букве «I» от SOLID, которая представляет идею разделения интерфейсов (interface segregation principle, ISP).

Так что же говорит принцип разделения интерфейсов:

Программные сущности не должны зависеть от методов, которые они не используют.

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

Давайте возьмем пример интерфейса TextWatcher в Android. Этот интерфейс широко используется в разработке, когда мы хотим получать колбеки, если пользователь изменяет текст в любом View, наследнике TextView (например, в EditText). Мы можем добавить интерфейс TextWatcher в EditText, используя функцию addTextChangedListener.

Пример: необходимо отображать всплывающее сообщение с текущим текстом всякий раз, когда пользователь вводит символ в EditText.

Для этого нам нужно прикрепить интерфейс TextWatcher к EditText с помощью функции addTextChangedListener. Здесь мы вынуждены реализовать все 3 функции интерфейса TextWatcher.

Код будет выглядеть примерно так:

binding.editText.addTextChangedListener(object: TextWatcher {
    override fun beforeTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int) {
        //NO OP
    }

    override fun onTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int) {
        showToast(charSequence.toString())
    }

    override fun afterTextChanged(charSequence: Editable?) {
        //NO OP
    }
})

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

Теперь возникает вопрос — как мы можем разделить функции в интерфейсе TextWatcher и использовать только функцию onTextChanged?

С помощью мощной функции расширения в Kotlin и использования inline функции, которая повышает эффективность кода Kotlin при использовании функций высшего порядка, давайте оптимизируем код, чтобы удовлетворить принцип разделения интерфейсов.

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

Посмотрите приведенный ниже код:

inline fun EditText.onTextChange(crossinline listener: (String) -> Unit) {
    this.addTextChangedListener(object: TextWatcher {
        override fun beforeTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int) {
        //NO OP
    }

    override fun onTextChanged(charSequence: CharSequence?, p1: Int, p2: Int, p3: Int) {
        listener(charSequence.toString())
    }

    override fun afterTextChanged(p0: Editable?) {
        //NO OP
    }

    })
}

Здесь мы добавляем лямбда-функцию listener в качестве параметра нашей функции расширения onTextChange. Слушатель принимает String в качестве аргумента. Всякий раз, когда вызывается колбек onTextChanged в TextWatcher, мы будем вызывать функцию listener и передавать ей строковый аргумент.

Эту “расширенную” функцию можно использовать с EditText, например:

binding.editText.onTextChange {
    showToast(it)
}

Внутри onTextChange вы получите строку, которая передается из функции onTextChanged интерфейса TextWatcher, и мы можем добавить нашу функциональность для отображения тоста.

inline : Использование inline функции повышает производительность функции более высокого порядка. Inline функция указывает компилятору копировать параметры и функции в место вызова.

crossinline : Ключевое слово crossinline добавлено, чтобы избежать нелокальных возвратов. Без него нелокальное управление потоком выполнения в лямбдах запрещено. Чтобы избежать этого, мы используем ключевое слово crossinline перед лямбда-функцией listener.

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

Спасибо за чтение!!! Удачного кодирования!!!

Githu: https://github.com/vinay4791/SolidPrinciples

Источник

Exit mobile version