Паттерн Observer
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 68 из 87.
Паттерн «Наблюдатель» (Observer Pattern) — это поведенческий паттерн проектирования, который устанавливает связь «один ко многим» между объектами. Когда один объект (субъект) меняет свое состояние, все зависящие от него объекты (наблюдатели) автоматически уведомляются и обновляются. Представьте это как подписку на рассылку новостей: подписчики получают обновления всякий раз, когда публикуется новый контент.
Паттерн включает в себя два основных компонента: субъект, который хранит список наблюдателей и уведомляет их об изменениях, и наблюдателей, которые определяют метод обновления для реагирования на уведомления:
import java.util.ArrayList;
import java.util.List;
interface Observer {
void update(String message);
}
class NewsPublisher {
private List<Observer> subscribers = new ArrayList<>();
public void subscribe(Observer observer) {
subscribers.add(observer);
}
public void unsubscribe(Observer observer) {
subscribers.remove(observer);
}
public void publish(String news) {
for (Observer subscriber : subscribers) {
subscriber.update(news);
}
}
}
class EmailSubscriber implements Observer {
private String email;
public EmailSubscriber(String email) {
this.email = email;
}
public void update(String message) {
System.out.println("Email to " + email + ": " + message);
}
}Когда издатель выпускает новости, все подписчики уведомляются автоматически:
NewsPublisher publisher = new NewsPublisher();
publisher.subscribe(new EmailSubscriber("alice@mail.com"));
publisher.subscribe(new EmailSubscriber("bob@mail.com"));
publisher.publish("Breaking news!");
// Вывод:
// Email для alice@mail.com: Breaking news!
// Email для bob@mail.com: Breaking news!Паттерн «Наблюдатель» способствует слабой связанности — субъекту не нужно знать конкретные классы своих наблюдателей, достаточно лишь того, что они реализуют интерфейс Observer. Это позволяет легко добавлять новые типы наблюдателей без изменения кода субъекта.
Задание
ЛегкоДавайте создадим систему оповещения о ценах на акции, используя паттерн Наблюдатель (Observer Pattern)! Вы создадите систему, в которой биржевой тикер уведомляет нескольких инвесторов об изменении цены акции, позволяя каждому инвестору реагировать на обновления цен по-своему.
Вы организуете свой код в четырех файлах:
Observer.java: Определите интерфейсObserver, который будут реализовывать все инвесторы. Он должен объявлять один методupdate(String stockName, double price), который наблюдатели используют для получения уведомлений об изменениях цен.StockTicker.java: Создайте класс субъекта, который хранит список наблюдателей и уведомляет их об изменениях цен. ВашStockTickerдолжен иметь:Приватное поле String для названия акции (устанавливается через конструктор).
Приватное поле double для текущей цены (изначально 0).
Приватный список для хранения подписанных наблюдателей.
Метод
subscribe(Observer observer), который добавляет наблюдателя в список.Метод
unsubscribe(Observer observer), который удаляет наблюдателя из списка.Метод
setPrice(double price), который обновляет цену, а затем уведомляет всех наблюдателей, вызывая их методupdateс названием акции и новой ценой.Investors.java: Создайте два различных типа инвесторов, реализующих интерфейсObserver:DayTrader— имеет приватное поле имени, устанавливаемое через конструктор. При обновлении выводит:[name] received alert: [stockName] is now $[price] - Making quick trade!LongTermInvestor— имеет приватное поле имени, устанавливаемое через конструктор. При обновлении выводит:[name] received alert: [stockName] is now $[price] - Holding steady.Форматируйте цену с двумя знаками после запятой.
Main.java: Объедините вашу систему наблюдателей! Вы получите три входных значения: название акции (String), первое обновление цены (double) и второе обновление цены (double).Создайте
StockTickerс заданным названием акции. СоздайтеDayTraderс именем"Alice"иLongTermInvestorс именем"Bob", и подпишите обоих на тикер.Установите цену равной первому входному значению (все наблюдатели получат уведомление). Затем отпишите Alice. Наконец, установите цену равной второму входному значению (на этот раз уведомление должен получить только Bob).
Вы получите три входных значения по порядку: название акции (String), первая цена (double), вторая цена (double).
Эта задача демонстрирует мощь паттерна Наблюдатель: StockTicker ничего не знает о конкретных типах инвесторов — он только знает, что они реализуют интерфейс Observer. Когда вы отписываете Alice, она перестает получать обновления, в то время как Bob продолжает получать уведомления. Такая слабая связанность позволяет легко добавлять новые типы инвесторов без изменения тикера!
Шпаргалка
Паттерн Наблюдатель (Observer Pattern) — это поведенческий паттерн проектирования, который устанавливает связь «один ко многим» между объектами. Когда один объект (субъект) меняет свое состояние, все зависимые от него объекты (наблюдатели) автоматически уведомляются и обновляются.
Паттерн включает в себя два основных компонента:
- Субъект (Subject): хранит список наблюдателей и уведомляет их об изменениях
- Наблюдатели (Observers): определяют метод обновления для реагирования на уведомления
Базовая структура:
import java.util.ArrayList;
import java.util.List;
interface Observer {
void update(String message);
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer observer) {
observers.add(observer);
}
public void unsubscribe(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
class ConcreteObserver implements Observer {
public void update(String message) {
// Реакция на уведомление
}
}Пример использования:
Subject subject = new Subject();
subject.subscribe(new ConcreteObserver());
subject.notifyObservers("State changed!");
// Все подписанные наблюдатели получают обновлениеПаттерн Наблюдатель способствует слабой связанности (loose coupling) — субъекту не нужно знать конкретные классы своих наблюдателей, достаточно того, что они реализуют интерфейс Observer. Это позволяет легко добавлять новые типы наблюдателей без изменения кода субъекта.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Чтение входных данных
String stockName = scanner.nextLine();
double firstPrice = scanner.nextDouble();
double secondPrice = scanner.nextDouble();
// TODO: Создайте StockTicker с заданным названием акции
// TODO: Создайте DayTrader по имени "Alice"
// TODO: Создайте LongTermInvestor по имени "Bob"
// TODO: Подпишите Alice и Bob на тикер
// TODO: Установите цену на firstPrice (оба наблюдателя получат уведомление)
// TODO: Отпишите Alice
// TODO: Установите цену на secondPrice (только Bob получит уведомление)
scanner.close();
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»