Приведи пример поведенческого паттерна

Ответ

Наблюдатель (Observer) — поведенческий паттерн, позволяющий объектам подписываться на события других объектов и реагировать на их изменения.

interface Observer {
    fun update(data: String)
}

class Subject {
    private val observers = mutableListOf<Observer>()
    private var state: String = ""

    fun setState(newState: String) {
        state = newState
        notifyObservers()
    }

    fun addObserver(observer: Observer) {
        observers.add(observer)
    }

    private fun notifyObservers() {
        observers.forEach { it.update(state) }
    }
}

class ConcreteObserver : Observer {
    override fun update(data: String) {
        println("Received update: $data")
    }
}

// Использование:
val subject = Subject()
val observer = ConcreteObserver()
subject.addObserver(observer)
subject.setState("New State") // Выведет: "Received update: New State"

Применение: уведомления UI о изменениях данных, события в EventBus, LiveData в Android.

Ответ 18+ 🔞

А, слушай, вот паттерн, который реально жизнь упрощает, если его правильно прикрутить. Называется «Наблюдатель», или Observer, если по-умному. Суть проще пареной репы, но гениальная, ёпта.

Представь себе такую ситуацию: есть у тебя один главный объект — Subject (или «Издатель»). Он типа как центр вселенной, в котором хранится какое-то важное состояние. И есть куча других объектов — Observers («Наблюдатели» или «Подписчики»), которым позарез нужно знать, когда это состояние меняется. Они как назойливые родственники, которые хотят быть в курсе всех твоих дел.

Вот этот главный объект не должен бегать за каждым подписчиком и кричать «Эй, у меня данные поменялись!». Это же пиздопроебина получается, он только этим и будет заниматься. Вместо этого он заводит себе список этих самых наблюдателей. Кто хочет знать о изменениях — тот пусть приходит и говорит: «Братан, добавь меня в список, я твой фанат». Это метод addObserver.

А когда у главного объекта меняется его внутреннее состояние (через setState), он просто проходит по своему списку фанатов и каждому тихонько так говорит: «Эй, дружок, данные обновились, вот держи». Это метод notifyObservers. А наблюдатели уже сами решают, что им с этой информацией делать — обновить интерфейс, записать в лог или просто охуеть от удивления.

Вот смотри на код, тут всё наглядно. Объявляем интерфейс Observer. Это такой контракт: «Мужик, если хочешь быть наблюдателем, у тебя ДОЛЖЕН быть метод update(data: String). А что ты в нём делаешь — твои проблемы».

interface Observer {
    fun update(data: String)
}

Потом делаем самого «босса» — класс Subject. У него три дела:

  1. Хранить состояние (state).
  2. Хранить список подписчиков (observers).
  3. Когда состояние меняется — орать об этом всем в списке.
class Subject {
    private val observers = mutableListOf<Observer>() // Список подхалимов
    private var state: String = "" // Самое важное, за чем все следят

    fun setState(newState: String) {
        state = newState // Меняем состояние
        notifyObservers() // И орём всем: "Чуваки, всё изменилось!"
    }

    fun addObserver(observer: Observer) {
        observers.add(observer) // Новый фанат в клуб
    }

    private fun notifyObservers() {
        observers.forEach { it.update(state) } // Проходим по списку и пинаем каждого
    }
}

Ну и конкретный наблюдатель, который, допустим, просто выводит сообщение в консоль. Скучный тип.

class ConcreteObserver : Observer {
    override fun update(data: String) {
        println("Received update: $data") // Получил уведомление и вывел
    }
}

А вот как это всё в жизни работает:

val subject = Subject() // Создаём главный объект
val observer = ConcreteObserver() // Создаём одного любопытного
subject.addObserver(observer) // Подписываем его на обновления
subject.setState("New State") // Меняем состояние -> наблюдатель получает пинок и выводит: "Received update: New State"

Где это полезно, спросишь? Да овердохуища где!

  • UI в мобилках (типа Android с LiveData): Твоя разметка (View) подписывается на данные (ViewModel). Данные изменились — View сама перерисовывается. Волнение ебать от такой магии, правда?
  • Разные EventBus'ы: Один компонент кинул событие (setState("USER_CLICKED")), а двадцать других его тут же обработали.
  • Любая система уведомлений: Чат, почта, оповещения в играх. Всё построено на этой идее.

Главная фишка в том, что издатель и подписчики не знают друг о друге подробностей. Издателю да похуй, кто там в списке — он просто вызывает метод update. А подписчику да похуй, как именно издатель устроен внутри — он просто получает данные. Связь слабая, и это охуенно.

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

Короче, паттерн мощный, как хуй с горы. Используй.