Фундаментальный шаблон проектирования, который должен знать каждый инженер-программист.

Шаблон проектирования Observer - это фундаментальный инструмент для любого способного инженера-программиста. Одним предложением он состоит из наблюдателей, которые уделяют пристальное внимание темам, которые меняют свое состояние. В этом руководстве мы рассмотрим варианты использования, диаграмму UML и реализацию на моем текущем любимом языке программирования - Go.

Примечание. Терминология может варьироваться в зависимости от наблюдателя / подписчика или темы / темы / издателя. Все они одинаковы, но в этой статье я буду использовать наблюдателя и темы.

Случаи применения

Реальные примеры шаблона проектирования наблюдателя бесконечны. Они включают:

  • Платформы социальных сетей - друзья могут подписываться на других друзей, чтобы делиться друг с другом обновлениями. Сам Medium мог бы использовать этот шаблон!
  • Новостные платформы - люди подписываются на новостные станции (например, Washington Post) и получают ежедневные / еженедельные уведомления о последних новостях.
  • Роботизированная операционная система (ROS) - это не настоящая операционная система, но ROS фактически имеет базовую связь со своими роботами, построенную на шаблоне наблюдателя с издателями и подписчиками для передачи информации.

И многое другое!

Диаграмма UML

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

Начиная с верхнего левого угла, Subject обычно указывается как абстрактный класс, но в Go нет классов, поэтому мы вместо этого помечаем его как интерфейс. ConcreteSubject реализует этот интерфейс с тем же процессом, применяемым к Observers.

У нас есть методы Attach() и Detach() в Subject, которые оба принимают объект Observer. Между Subject и Observer существует составная связь, что означает, что у нас может быть Subject от нуля до многих Observers. Кроме того, как ConcreteSubject, так и ConcreteObserver реализуют два интерфейса с пунктирными линиями, а не наследуют от класса, который будет заполнен строками (потому что это все-таки Go).

Наконец, конкретным объектам назначены частные состояния, которые затем могут быть доступны и изменены общедоступными методами для сохранения целостности.

Примечание. В Go нет общедоступных или частных ключевых слов. Вместо этого общедоступные методы начинаются с заглавной буквы, а частные методы - со строчной.

Реализация в Go

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

Сделаем базовую структуру, которая будет состоять из 3-х файлов.

$ mkdir observer-pattern-go
$ cd observer-pattern-go
$ touch main.go observer.go subject.go

Сначала мы будем работать над реализацией наблюдателя.

Observer.go

Наш Observer интерфейс прост, нам просто нужно реализовать структуру с включенным Update() методом. WeatherStation справится с этим. Поскольку в Go нет классов, мы создадим нашу собственную форму конструктора, как показано в строках 9–11, которые возвращают ссылку на новый объект WeatherStation. Структура WeatherStation просто имеет имя как единственное свойство.

Затем мы реализуем интерфейс Observer, реализуя метод Update(). Это достигается путем вставки ссылки на объект WeatherStation в функцию в строке 17. Теперь WeatherStation реализует метод Update(), и интерфейс Observer удовлетворен.

Функция Update() просто сообщит о температуре и о том, жаркий ли день или холодный.

subject.go

WeatherSensor реализует интерфейс Subject, определяя метод NotifyAll(). WeatherSensor также обладает дополнительными функциями, так как в нем есть часть []*WeatherStation, в которой хранятся ссылки на метеостанции. Ключевое слово Subject в строке 18 означает, что мы расширяем интерфейс до этой структуры. Наконец, есть еще государственная собственность temperature.

Затем мы также реализуем два метода addStation() и removeStation(), которые оба принимают ссылку на WeatherStation в качестве параметра. Добавить метеостанцию ​​так же просто, как добавить ее к фрагменту объекта. Однако удаление метеостанции на самом деле немного сложнее. Ссылайтесь на приведенный ниже код, но я удивлен, что в Go нет более простого способа справиться с этим за нас.

Реализация метода NotifyAll() просто перебирает все weatherStations и вызывает свой собственный метод Update(), который мы определили ранее для определения того, был ли день жарким, холодным или мягким.

Конечно, у нас также есть версия конструктора Go в строках 46–50, который возвращает новую ссылку &WeatherSensor, определяющую начальную температуру. Функция getRandomTemperature() является просто вспомогательной функцией и возвращает случайное целое число из заданного диапазона, как определено выше.

Обратите внимание, как всякий раз, когда мы вызываем метод ChangeTemperature(), мы передаем ссылку на структуру *WeatherSensor. Прежде чем распечатать, что наступил новый день, мы меняем температуру датчика, а затем уведомляем всех наблюдателей (метеостанции) об изменении с помощью NotifyAll().

main.go

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

Вы можете запустить проект с помощью команды в каталоге observer-pattern-go:

$ go run *.go
Denver Weather Station reporting:
    The temperature is 101 F, it will be a hot day!
Vail Weather Station reporting:
    The temperature is 101 F, it will be a hot day!
Cheyenne Weather Station reporting:
    The temperature is 57 F, it will be a mild day.
It's a new day!
Denver Weather Station reporting:
    The temperature is 17 F, it will be a cold day!
Vail Weather Station reporting:
    The temperature is 17 F, it will be a cold day!
It's a new day!
Cheyenne Weather Station reporting:
    The temperature is 29 F, it will be a cold day!
It's a new day!
Denver Weather Station reporting:
    The temperature is 1 F, it will be a cold day!
Denver Weather Station reporting:
    The temperature is 1 F, it will be a cold day!
Cheyenne Weather Station reporting:
    The temperature is 29 F, it will be a cold day!

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

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

Надеюсь, вам понравилась эта статья. Если вам что-то понравилось или вы хотите узнать больше, я рекомендую вам оставить комментарий ниже. Спасибо за прочтение!