
Продвигаясь вперед в моем продолжающемся исследовании шаблонов проектирования, мы теперь погружаемся в шаблон, который отличается своей гибкостью, динамизмом, масштабируемостью и реактивностью. Да, как вы уже догадались, "Шаблон проектирования Observer" привлекает всеобщее внимание. Я черпаю вдохновение на страницах книги «Шаблоны проектирования Head First», а также привожу дополнительные примеры, чтобы обогатить понимание. Как всегда, я буду делиться ссылками на ресурсы, которые я использовал.

Шаблон проектирования наблюдателя
Обзор приложения для мониторинга погоды: это приложение, которое мы собираемся создать.
Приложение для мониторинга погоды предназначено для предоставления информации о погоде в режиме реального времени на различные дисплеи, каждый из которых представляет данные о погоде в определенном формате. Приложение использует шаблон Observer для установления связи между центральной метеостанцией (субъектом) и несколькими дисплеями погоды (наблюдателями). Когда данные о погоде изменяются, дисплеи автоматически обновляются, чтобы отражать последние условия.
Погодная станция (тема):
- Это представляет собой центральную метеостанцию, действующую как субъект в шаблоне Observer.
- Он поддерживает данные о погоде, такие как температура и влажность.
- Предоставляет методы для регистрации наблюдателей (отображений) и обновления данных о погоде.
- Уведомляет всех зарегистрированных наблюдателей об изменении данных о погоде.
WeatherDisplay (наблюдатели):
- Это различные погодные дисплеи, которые действуют как наблюдатели в шаблоне Observer.
- Различные дисплеи могут отображать текущие условия, статистику, прогнозы и т. д.
- Дисплеи обновляются автоматически при изменении данных о погоде на метеостанции.
Интерфейс наблюдателя:
- Определяет интерфейс, который должны реализовать конкретные дисплеи, чтобы стать наблюдателями.
- Содержит метод обновления, который вызывается субъектом для уведомления наблюдателей об изменениях.
Интерфейс темы:
- Определяет интерфейс, который конкретная метеостанция реализует в качестве субъекта.
- Включает методы для регистрации, удаления и уведомления наблюдателей.
Основное приложение: WeatherStationMain:
- Основной класс, демонстрирующий функциональность приложения.
- Создает экземпляры метеостанции и различных элементов отображения.
- Регистрирует дисплеи в качестве наблюдателей метеостанции.
- Имитирует изменения в данных о погоде для наблюдения за автоматическими обновлениями дисплеев.

Цель:
- Развязка. Шаблон Observer отделяет отображение погоды от метеостанции. Отображение зависит от абстрактного интерфейса метеостанции (Subject), а не от его конкретной реализации.
- Динамические обновления: дисплеи становятся наблюдателями и регистрируются на метеостанции. Когда данные о погоде изменяются, метеостанция автоматически уведомляет все зарегистрированные дисплеи, и они обновляются новыми данными.
- Модульность. Шаблон Observer позволяет легко добавлять или удалять дисплеи, не влияя на код метеостанции. Новые типы дисплеев можно добавлять независимо.
- Управление событиями.Шаблон Observer использует подход, управляемый событиями. Изменения в состоянии метеостанции (изменения данных о погоде) запускают серию событий, которые обновляют дисплеи.
Увеличить цель:
- Дополнительные типы отображения. Вы можете реализовать отображения, которые предоставляют более подробные прогнозы, исторические данные или графические представления тенденций погоды.
- Пользовательская настройка. Реализуйте функцию, позволяющую пользователям настраивать внешний вид или данные, отображаемые на каждом типе отображения погоды, в соответствии со своими предпочтениями.
В книге используется фантастическая аналогия, которая мне очень помогла в понимании закономерности.
- Издатель газеты начинает бизнес и начинает издавать газеты.
- Вы подписываетесь на определенного издателя, и каждый раз, когда появляется новое издание, оно доставляется вам. Пока вы остаетесь подписчиком, вы получаете новые газеты.
- Вы отписываетесь, когда вам больше не нужны документы, и они перестают доставляться.
- Пока издатель продолжает свою деятельность, люди, гостиницы, авиакомпании и другие предприятия постоянно подписываются на газету и отписываются от нее.

Определение шаблона Observer: он определяет зависимость "один ко многим" между объектами, так что при изменении состояния одного объекта все его зависимые объекты получают уведомление и автоматически обновляются.


Прежде чем мы засучим рукава и начнем писать код, давайте сначала углубимся в важную концепцию:слабая связь.
Слабая связанность — это фундаментальное понятие в разработке программного обеспечения, которое описывает, как компоненты или модули внутри системы взаимодействуют друг с другом. Это относится к степени зависимости между этими компонентами. Слабосвязанная конструкция обеспечивает гибкость, модульность и удобство сопровождения программных систем.
Ключевые характеристики слабой связи:
- Независимость. В слабосвязанной системе компоненты предназначены для максимально возможной независимой работы. Внутренние детали каждого компонента скрыты от других компонентов.
- Уменьшенная зависимость: компоненты взаимодействуют друг с другом через четко определенные интерфейсы, а не напрямую обращаются к внутренней реализации друг друга.
- Гибкость. Изменения в одном компоненте с меньшей вероятностью повлияют на другие компоненты. Эта гибкость позволяет вносить изменения или дополнения, не вызывая широкомасштабных сбоев.
- Модульность. Компоненты можно разрабатывать, тестировать и поддерживать независимо друг от друга. Их можно заменить или модернизировать, не затрагивая всю систему.
- Взаимозаменяемость. Слабосвязанные компоненты можно легко заменить альтернативными реализациями, если они придерживаются одного и того же интерфейса.
Преимущества слабой связи:
- Простота обслуживания. Когда компоненты слабо связаны, изменения, внесенные в один компонент, с меньшей вероятностью повлияют на другие. Это снижает риск непреднамеренных побочных эффектов во время технического обслуживания.
- Масштабируемость. Слабосвязанные системы более масштабируемы, поскольку добавление или изменение компонентов не нарушает существующую архитектуру.
- Повторное использование кода. Слабо связанные компоненты часто можно повторно использовать в разных контекстах, если они удовлетворяют одним и тем же интерфейсам.
- Параллельная разработка. Разные команды или разработчики могут работать над разными компонентами одновременно, если они придерживаются установленных интерфейсов.
- Простое тестирование. Изолированные компоненты можно тестировать изолированно, что упрощает выявление и исправление ошибок.
Примеры слабой связи:
- Программирование на основе интерфейса: использование интерфейсов или абстрактных классов для определения контрактов, которым должны следовать компоненты, что позволяет взаимозаменяемо использовать различные реализации.
- Внедрение зависимостей:внедрение зависимостей в компонент вместо их жесткого кодирования. Это позволяет заменять компоненты без изменения основной логики.
- Архитектура, управляемая событиями: компоненты обмениваются данными посредством событий и сообщений, что снижает прямые зависимости между ними.
- Шаблоны проектирования.Шаблоны проектирования, такие как "Наблюдатель", "Стратегия" и "Командование", способствуют ослаблению связи за счет разделения обязанностей и взаимодействий.
Слабая связь в примере шаблона Observer:
В примере шаблона Observer (приложение для мониторинга погоды) слабая связь достигается за счет следующего:
- Интерфейс Subject определяет контракт для субъектов и позволяет наблюдателям регистрироваться и получать обновления, не зная специфики реализации субъекта.
- Интерфейс наблюдателя определяет контракт для наблюдателей, позволяя различным элементам отображения придерживаться одного и того же интерфейса.
- Наблюдатели не тесно связаны с реализацией WeatherStation. Они полагаются на интерфейс Subject для получения обновлений.
- WeatherStation ведет список зарегистрированных наблюдателей, но сами наблюдатели не знают друг о друге.
Посмотрите это видео: жесткое соединение против слабого соединения
Теперь давайте применим шаблон в действии, реализовав его с помощью кода Java.
Тема интерфейса
Интерфейс Subject определяет контракт для субъекта (наблюдаемого объекта) в шаблоне Observer. Он устанавливает методы, которые субъекты должны реализовать для управления набором наблюдателей и уведомления их об изменениях в состоянии субъекта.
Рабочий процесс:
- Зарегистрируйте наблюдателя (
registerObserver):
- Субъекты предоставляют наблюдателям возможность зарегистрироваться.
- Наблюдатели, желающие получать обновления от субъекта, вызывают этот метод, передавая себя в качестве аргументов.
- Субъект добавляет наблюдателя в свою коллекцию зарегистрированных наблюдателей.
2. Удалить наблюдателя (removeObserver):
- Субъекты позволяют наблюдателям удалять себя из списка зарегистрированных наблюдателей.
- Наблюдатели вызывают этот метод, чтобы указать, что они больше не заинтересованы в получении обновлений.
3. Уведомить наблюдателей(notifyObserver):
- Когда состояние субъекта изменяется, субъект вызывает метод
notifyObserver. - Этот метод перебирает список зарегистрированных наблюдателей и вызывает их методы обновления.
- Наблюдатели информируются об изменении состояния и могут соответствующим образом обновить себя.
Преимущества. Интерфейс Subject гарантирует, что субъекты и наблюдатели отделены друг от друга, способствуя четкому разделению задач. Субъектам не нужно знать конкретные детали своих наблюдателей, а наблюдатели не зависят от реализации субъекта.
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObserver();
}
Интерфейс наблюдателя
Интерфейс Observer определяет контракт для наблюдателей в шаблоне Observer. Наблюдатели — это объекты, заинтересованные в получении обновлений от субъекта при изменении состояния субъекта. Этот интерфейс гарантирует, что все наблюдатели реализуют согласованный метод обновления себя на основе нового состояния субъекта.
Метод:
- Обновление (
update):
- Этот метод вызывается субъектом при изменении его состояния.
- Субъект передает соответствующую информацию о состоянии (такую как температура, влажность, давление) в качестве аргументов.
- Наблюдатели реализуют этот метод, чтобы реагировать на обновленное состояние и выполнять необходимые действия на основе новых данных.
Рабочий процесс:
- Регистрация:
- Наблюдатели регистрируются у субъекта с помощью метода регистрации субъекта (обычно
registerObserver). - Это устанавливает связь между субъектом и наблюдателем.
2. Уведомление:
- Когда состояние субъекта изменяется, субъект вызывает метод
updateдля каждого зарегистрированного наблюдателя. - Субъект передает обновленную информацию о состоянии наблюдателю.
3. Действие наблюдателя:
- Наблюдатели получают обновленное состояние и обрабатывают информацию.
- Они могут обновлять свое внутреннее состояние, выполнять вычисления или инициировать другие действия на основе новых данных.
Преимущества. Интерфейс Observer обеспечивает соблюдение общего протокола для всех наблюдателей, обеспечивая им единый способ получения и обработки обновлений от субъектов. Эта согласованность способствует модульности и облегчает введение новых реализаций наблюдателя, не затрагивая субъекта или других наблюдателей.
public interface Observer {
void update(float temp, float humidity, float pressure);
}
Интерфейс DisplayElement
Интерфейс DisplayElement используется в контексте шаблона Observer для определения контракта для элементов отображения, которые визуализируют или представляют информацию на основе обновлений, полученных от субъектов. Элементы отображения — это наблюдатели, которые визуально отображают изменения состояния субъекта.
Метод:
- Показать (
display):
- Этот метод реализуется элементами отображения, чтобы определить, как обновленная информация должна быть представлена визуально.
- Элементы отображения используют этот метод для визуализации состояния субъекта в удобной для пользователя форме.
Рабочий процесс:
- Регистрация:
- Элементы отображения регистрируются как наблюдатели с субъектом с использованием метода регистрации субъекта (например,
registerObserver). - Это позволяет отображаемым элементам получать обновления при изменении состояния объекта.
2.Уведомление и отображение:
- Когда состояние субъекта изменяется, субъект вызывает метод
updateдля каждого зарегистрированного элемента отображения (наблюдателя). - Элемент отображения обрабатывает обновленное состояние и вызывает свой метод
displayдля визуального представления информации.
3. Презентация:
- Метод
displayэлемента display определяет, как обновленная информация отображается пользователям. - Это может включать рендеринг данных на экране, их печать или отображение с помощью других визуальных средств.
Преимущества.Интерфейс DisplayElement обеспечивает согласованный метод для всех элементов отображения для представления обновленной информации в стандартизированном виде. Это позволяет легко интегрировать различные типы элементов отображения с различными предметами.
public interface DisplayElement {
void display();
}
Класс WeatherData
Класс WeatherData представляет субъекта в шаблоне Observer. Он инкапсулирует данные, связанные с погодой, и предоставляет методы для управления списком наблюдателей, уведомления их об изменениях состояния и обновления их новыми измерениями погоды.
Атрибуты:
observers: список для хранения зарегистрированных наблюдателей.temperature,humidity,pressure: измерения, связанные с погодой.
Методы:
- Конструктор(
WeatherData): инициализирует списокobserversпри создании объектаWeatherData. - Зарегистрировать наблюдателя (
registerObserver): добавляет наблюдателя в список зарегистрированных наблюдателей. - Удалить наблюдателя (
removeObserver): удаляет наблюдателя из списка зарегистрированных наблюдателей. - Уведомить наблюдателей (
notifyObserver): перебирает список зарегистрированных наблюдателей и вызывает методupdateдля каждого наблюдателя, передавая измерения погоды. - Измерения изменены (
measurementsChanged): вызывает методnotifyObserver, чтобы сообщить наблюдателям, что измерения погоды изменились. - Задать измерения(
setMeasurements): обновляет измерения погоды и вызываетmeasurementsChangedдля уведомления наблюдателей. - Методы получения (
getTemperature,getHumidity,getPressure): разрешить наблюдателям доступ к измерениям погоды.
Рабочий процесс:
- Наблюдатели регистрируются в объекте
WeatherDataметодомregisterObserver. - Когда измерения погоды изменяются (устанавливаются с помощью
setMeasurements), вызывается методmeasurementsChanged, который запускает методnotifyObserver. - Метод
notifyObserverперебирает список наблюдателей и вызывает методupdateдля каждого наблюдателя, передавая обновленные данные о погоде. - Наблюдатели, такие как дисплеи погоды, получают обновленные измерения и обрабатывают их для визуализации текущей информации о погоде.
public class WeatherData implements Subject{
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
this.observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
for(Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObserver();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
Основной класс WeatherStation
Класс WeatherStation служит клиентским кодом, который демонстрирует использование шаблона Observer путем создания экземпляров субъекта и наблюдателей и имитации изменений в измерениях погоды.
Рабочий процесс:
- Создание данных о погоде (
WeatherData): создается экземпляр классаWeatherData. Это будет действовать как объект, который содержит измерения погоды. - Создание наблюдателей(
CurrentConditionDisplay): создается экземпляр классаCurrentConditionDisplay(наблюдатель), которому передается объектWeatherDataв качестве параметра. Это устанавливает отношения наблюдатель-субъект. - Установка измерений(
setMeasurements): методsetMeasurementsобъектаWeatherDataвызывается для имитации изменения погодных измерений (температура, влажность, давление). - Обновление наблюдателей. Метод
setMeasurementsвызываетmeasurementsChanged, который, в свою очередь, вызываетnotifyObserverдля обновления всех зарегистрированных наблюдателей. - Обновление наблюдателя(
update): вызывается методupdateнаблюдателя (CurrentConditionDisplay) с новыми измерениями погоды. - Отображение погоды (
display): методupdateнаблюдателя обрабатывает новые измерения и вызывает методdisplayдля визуализации обновленной информации о погоде.
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
//Current conditions: 80.0 F degrees and 65.0% humidity
}
}
Что можно и чего нельзя делать с шаблоном проектирования Observer
Do’s:
- Использование наблюдателя для разделения: используйте шаблон наблюдателя, чтобы добиться слабой связи между субъектами и наблюдателями, что позволит им взаимодействовать без прямых зависимостей.
- Предпочтение интерфейсам: определите интерфейсы, которых субъекты и наблюдатели должны придерживаться. Это способствует соблюдению контрактов и обеспечивает гибкое взаимодействие.
- Регистрация и отмена регистрации наблюдателей: предоставление методов для регистрации и удаления наблюдателей в субъектах. Это облегчает динамическое управление подписками наблюдателей.
- Внимательно уведомлять наблюдателей. Уведомлять наблюдателей об изменении состояния субъекта, чтобы наблюдатели получали точную и актуальную информацию.
- Следуйте принципу единой ответственности: сосредоточьте внимание классов наблюдателей на наблюдении и обработке изменений состояния. Избегайте добавления несвязанной логики в классы-наблюдатели.
- Автоматическое обновление наблюдателей. Убедитесь, что наблюдатели автоматически получают обновления при изменении состояния субъекта. Избегайте ручных обновлений.
Не:
- Не полагайтесь на конкретные классы. Избегайте жесткой привязки наблюдателей к конкретным предметным классам. Вместо этого используйте интерфейсы или абстрактные классы для повышения гибкости и расширяемости.
- Не перегружайте наблюдателей. Избегайте использования обширной обработки или тяжелых вычислений в методе
updateнаблюдателей. Держите его сосредоточенным на обработке нового состояния. - Не обновляйте слишком часто. Избегайте слишком частого обновления наблюдателей, так как это может привести к проблемам с производительностью или избыточной обработке.
- Не предполагайте количество наблюдателей. Не предполагайте фиксированное количество наблюдателей. Разработайте шаблон для обработки динамического количества наблюдателей.
- Не пренебрегайте эффективностью. Хотя уведомление всех наблюдателей важно, подумайте об эффективности в крупномасштабных системах. При необходимости оптимизируйте механизмы уведомлений.
- Не смешивайте логику наблюдателя. Избегайте смешивания логики регистрации, отмены регистрации и уведомлений с несвязанными функциями в субъектах.

Спасибо, что нашли время дочитать мою статью до конца. Я искренне надеюсь, что вы извлекли из него ценные идеи и знания. Если статья показалась вам интересной и информативной, прошу вас поделиться ею с друзьями и коллегами.
Еще раз спасибо за ваше время и поддержку. Следите за новыми статьями и обновлениями, пока я отправляюсь в это захватывающее приключение Java и Spring.
Спасибо, что дочитали до конца. Пожалуйста, следите за автором и этой публикацией. Посетите Stackademic, чтобы узнать больше о том, как мы демократизируем бесплатное обучение программированию по всему миру.
Репозиторий Github: https://github.com/LuisSalas94/Java-Design-Patterns

💖 Скажи мне 👋!
Фернандо Салас
Электронная почта: [email protected]
LinkedIn: https://www.linkedin.com/in/luisfernandosalasgave/
GitHub: https://github.com/LuisSalas94
Канал: https://medium.com/@luisfernandosalasg
Buymeacoffee: https://www.buymeacoffee.com/luisfernanM
Посмотрите другие мои статьи:
- Шаблон кодирования: циклическая сортировка
- Шаблон кодирования: два указателя
- Шаблон кодирования: скользящее окно
- 3 нетехнических книги, которые сделают вас лучшим программистом
- Пошаговое руководство: разработка API книжного магазина с Spring Boot и MySQL для операций CRUD
- Эффективная передача данных в REST API: подробное изучение шаблона DTO с помощью Spring Boot и MySql
- Обеспечение целостности данных в Spring Boot: стратегии обработки исключений и проверки данных
- Полное руководство по Spring Data JPA: Создание приложения книжного магазина с нуля — Часть I
- Полное руководство по Spring Data JPA: Создание приложения книжного магазина с нуля — Часть II
- Полное руководство по Spring Data JPA: создание приложения книжного магазина с нуля — часть III
- Создание полнофункциональной системы управления студентами с помощью React и Spring Boot
- Изучение шаблонов проектирования: внутри шаблона стратегии