С возвращением!🤙 Это мой второй пост из серии шаблонов проектирования. Большое спасибо O'Reilly Media за великолепную книгу. Не скупитесь и поддержите авторов и издателя: https://www.oreilly.com/library/view/head-first-design/9781492077992/

Первый пост в серии, который я рекомендую прочитать, чтобы получить общее представление о том, как все работает в серии: https://medium.com/towardsdev/strategy-pattern-for-independent-algorithms-kotlin-70ed24c7bd8b

Состав:

  • вступление
  • Проблема
  • Принципы дизайна, которым нужно следовать
  • Окончательный код решения
  • Рисунок

Без лишних слов, окунемся в статью✔️

Шаблон наблюдателя

Введение

Быть инженером-программистом значит очень много. Я имею в виду, на самом деле вам нужно не просто создавать продукт, а делать это таким образом, чтобы его можно было поддерживать, изменять в будущем🔧 И я не проповедую, что паттерны — это та серебряная пуля, которая решает со всеми вашими проблемами, определенно нет🖐🏼 Я пытаюсь утверждать, что тщательно и умно спроектированная система становится намного проще в работе, а шаблоны действительно помогают в этом процессе.

Использование шаблонов упрощает и ускоряет процесс разработки. Вам не нужно болтать, просто используйте название шаблона, которое абстрагирует (о да, даже здесь🙄) скрытое значение.

Проблема

У нас есть исходный код, предоставленный нам клиентом. Этот код, например, дает нам измерения погоды: температура☀️, давление🌬, влажность☔️
Затем эти getters запускаются основным методом measurementsChanged . Не сосредотачивайтесь на том, как запускается этот метод, так как это пока не наше дело.

Выше была лишь короткая запись, чтобы дать вам представление.

Итак, наша задача — отправить эти данные на созданные нами дисплеи: текущие условия, статистика погоды, прогноз.

Неверная первая реализация

Вы можете придумать что-то похожее на приведенное выше:

  • get... методы вернут какие-то данные (нам все равно, откуда они взяты)
  • мы присоединяем эти данные к необходимым переменным, а затем обновляем наши 3 дисплея.

Что не так?🤨

  1. Что, если мы хотим добавить 1, 2, x количество дисплеев? Вы будете тщательно их писать, не так ли? => повторяющийся код
  2. Если что-то является областью изменений, вам следует инкапсулировать это в отдельный класс, метод и т. д.

Встречайте шаблон Наблюдатель🥳🥳

              Publisher
   /             |            \
subscriber1 subscriber2  n-1 subscriber

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

  • Издатель реализует интерфейс издателя
  • Подписчики реализовать интерфейс подписчика

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

Суть шаблона🤌: есть один Subject, который уведомляет всех наблюдателей об изменениях. Если один из Наблюдателей решил уйти -› отправляет такое сообщение и Тема удаляет его. Наоборот, если есть объект, который хочет стать Наблюдателем -› он отправляет требуемое сообщение

Примечание: шаблон Pub-Sub похож на этот, но отличается от него.

Примечание второе: шаблон наблюдателя имеет отношение "один ко многим", где один — субъект, многие — наблюдатели.

Принципы дизайна

В этой статье у нас будет только один, но очень важный ориентир💥

  1. Стремитесь к слабосвязанным проектам между взаимодействующими объектами
    =› Проще говоря, ваш код не должен быть скреплен гвоздями, а должен быть гибким. Что это принесет? =› вспомнить лучшую ремонтопригодность с самого начала? Этот принцип и есть та волшебная палочка, которая помогает вам этого добиться🪄

Хорошо, а что это значит в рамках нашего паттерна?🧐

  1. Subject ничего не знает о наблюдателях, кроме того, что у них есть необходимый интерфейс (и наоборот). Оба могут иметь миллионы других вещей, но это не имеет значения
  2. Наблюдатели могут быть легко добавлены или удалены
  3. Subject и Observer можно использовать/модифицировать другими способами, пока они не реализуют интерфейс

Окончательный код решения

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



Давайте просмотрим код и выясним, как приведенная выше схема применяется на практике.

Во-первых, я хотел бы указать, что есть 2 способа создания шаблона Observer. В чем разница⁉️
а. Реализация Push: Subject отправляет данные Observer через update()
b. Реализация Pull: Subject не принуждает данные, но наблюдатели уведомляются через update(), и в этом методе наш наблюдатель извлекает данные через геттеры (в Java) || методы в Котлине

Почему Push хуже реализации Pull?

  1. Что, если нам нужны другие дисплеи, где не все данные из Subject нужны?
  2. Что, если Субъект перестанет отправлять некоторые данные или произойдут какие-то другие изменения?

В моем коде есть реализация Pull

そう、анализ кода:

  1. observer.kt и subject.kt — это интерфейсы, которые определяют схему для будущих Subject и Observers.
  • Субъектный интерфейс имеет 3 метода: registerObserver() , removeObserver() , notifyObservers()
  • Интерфейс наблюдателя определяет один метод: update()

2. weatherData.kt — это реализация Subject.

  • он содержит список observers, который сначала пуст
  • у него есть такие переменные, как pressure, temperature, humidity, которые затем будут иметь некоторое содержимое
  • здесь реализованы все 3 метода из интерфейса
  • putNewData() — это метод, который запускается помимо данных
    — мы присоединяем эти данные к переменным, упомянутым выше
    — вызывается метод dataChange(), который, в свою очередь, дергает notifyObservers() (здесь этот дополнительный слой может убрать в сторону, но я решил оставить как в их Java-коде)
    - пройтись по списку наблюдателей и уведомить их через update()

3. В firstCompanyObserver.kt находится один из дисплеев, который мы хотим построить:

  • private val currWeatherData: weatherData = weatherData() определяет, какой Subject будет
  • Блок init помогает нам зарегистрировать этот observer в упомянутом выше Subject в его списке наблюдателей.
  • у нас есть переменные, которые необходимо заполнить данными. Осторожно: может быть 2 из 3 переменных + они не abstract или что-то подобное в Subject, поэтому мы можем называть их по своему усмотрению.
  • вытащить getters (на Java) || простые методы в Kotlin для заполнения их данными, когда update() вызывается из notifyObservers() в weatherData.kt
  • также мы реализуем интерфейс Display для представления данных особым образом. Осторожно: это один из пунктов, обсуждаемых в Принципах дизайна, т. е. у нас могут быть дополнительные элементы внутри Subject или Observers.

Рисование👓

11И на рисунке у меня есть реализация Push. WData – это наш субъект, а SObserver – наш наблюдатель. Все вещи аналогичны описанной выше схеме.

Если вам что-то непонятно, пишите в комментариях

Кроме того, registerObserver() является примером композиции. Как?🧐 Subject отправляет данные (при отправке) || уведомить (если получить) каждого наблюдателя с помощью метода update() и:

  • первый не нуждается в информации о втором
  • первое и второе слабо связаны друг с другом

Аутро👣

Академическое определение шаблона наблюдателя: устанавливает отношение «один ко многим» между объектами таким образом, что при изменении одного объекта все зависимые (они же наблюдатели) уведомляются и получают новые данные.

Надеюсь, вы много почерпнули из этой статьи🤩

Ты можешь меня найти: