Фундаментальный шаблон проектирования, который должен знать каждый инженер-программист.
Шаблон проектирования 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, поддерживающий ее.
Надеюсь, вам понравилась эта статья. Если вам что-то понравилось или вы хотите узнать больше, я рекомендую вам оставить комментарий ниже. Спасибо за прочтение!