С возвращением!🤙 Это мой второй пост из серии шаблонов проектирования. Большое спасибо 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, 2, x количество дисплеев? Вы будете тщательно их писать, не так ли?
=>
повторяющийся код - Если что-то является областью изменений, вам следует инкапсулировать это в отдельный класс, метод и т. д.
Встречайте шаблон Наблюдатель🥳🥳
Publisher / | \ subscriber1 subscriber2 n-1 subscriber
Издатель, известный как Subject, уведомляет подписчиковтакже называемых наблюдателями об изменениях, а в классической реализации отправляет изменения.
- Издатель реализует интерфейс издателя
- Подписчики реализовать интерфейс подписчика
Однако позже мы увидим, что есть одна лучшая модификация шаблона наблюдателя.
Суть шаблона🤌: есть один Subject, который уведомляет всех наблюдателей об изменениях. Если один из Наблюдателей решил уйти -› отправляет такое сообщение и Тема удаляет его. Наоборот, если есть объект, который хочет стать Наблюдателем -› он отправляет требуемое сообщение
Примечание: шаблон Pub-Sub похож на этот, но отличается от него.
Примечание второе: шаблон наблюдателя имеет отношение "один ко многим", где один — субъект, многие — наблюдатели.
Принципы дизайна
В этой статье у нас будет только один, но очень важный ориентир💥
- Стремитесь к слабосвязанным проектам между взаимодействующими объектами
=› Проще говоря, ваш код не должен быть скреплен гвоздями, а должен быть гибким. Что это принесет? =› вспомнить лучшую ремонтопригодность с самого начала? Этот принцип и есть та волшебная палочка, которая помогает вам этого добиться🪄
Хорошо, а что это значит в рамках нашего паттерна?🧐
- Subject ничего не знает о наблюдателях, кроме того, что у них есть необходимый интерфейс (и наоборот). Оба могут иметь миллионы других вещей, но это не имеет значения
- Наблюдатели могут быть легко добавлены или удалены
- Subject и Observer можно использовать/модифицировать другими способами, пока они не реализуют интерфейс
Окончательный код решения
Как и прежде, не стесняйтесь смотреть на мою реализацию шаблона в Котлине.
Давайте просмотрим код и выясним, как приведенная выше схема применяется на практике.
Во-первых, я хотел бы указать, что есть 2 способа создания шаблона Observer. В чем разница⁉️
а. Реализация Push: Subject отправляет данные Observer через update()
b. Реализация Pull: Subject не принуждает данные, но наблюдатели уведомляются через update()
, и в этом методе наш наблюдатель извлекает данные через геттеры (в Java) || методы в Котлине
Почему Push хуже реализации Pull?
- Что, если нам нужны другие дисплеи, где не все данные из Subject нужны?
- Что, если Субъект перестанет отправлять некоторые данные или произойдут какие-то другие изменения?
В моем коде есть реализация Pull
そう、анализ кода:
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()
и:
- первый не нуждается в информации о втором
- первое и второе слабо связаны друг с другом
Аутро👣
Академическое определение шаблона наблюдателя: устанавливает отношение «один ко многим» между объектами таким образом, что при изменении одного объекта все зависимые (они же наблюдатели) уведомляются и получают новые данные.
Надеюсь, вы много почерпнули из этой статьи🤩
Ты можешь меня найти:
- LinkedIn: www.linkedin.com/in/sleeplesschallenger
- GitHub: https://github.com/SleeplessChallenger
- Литкод: https://leetcode.com/SleeplessChallenger/
- Телеграмма: @SleeplessChallenger