Паттерн Observer
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 68 из 87.
Паттерн Observer — это поведенческий шаблон проектирования, который устанавливает отношение «один ко многим» между объектами. Когда один объект (тематический объект, subject) изменяет состояние, все его зависимые объекты (наблюдатели, observers) автоматически получают уведомление и обновляются. Представьте это как подписку на рассылку: подписчики получают обновления всякий раз, когда публикуется новый контент.
Этот шаблон включает два основных компонента: субъект, который поддерживает список наблюдателей и уведомляет их об изменениях, и наблюдателей, которые определяют метод обновления для ответа на уведомления:
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! Вы создадите систему, в которой тикер акций уведомляет нескольких инвесторов каждый раз, когда цена акции изменяется, позволяя каждому инвестору реагировать на обновления цены по-своему.
Вы организуете код в четырех файлах:
Observer.java: Define интерфейсObserver, который будут Implement все инвесторы. Он должен Declare единственный methodupdate(String stockName, double price), который наблюдатели используют для получения уведомлений об изменениях цены.StockTicker.java: Create subject class, который поддерживает list наблюдателей и уведомляет их об изменениях цены. ВашStockTickerдолжен иметь:Приватное поле типа String для имени акции (задается через constructor).
Приватное поле типа double для текущей цены (initially 0).
Приватный list для хранения подписанных наблюдателей.
Метод
subscribe(Observer observer), который добавляет наблюдателя в list.Метод
unsubscribe(Observer observer), который удаляет наблюдателя из list.Метод
setPrice(double price), который обновляет цену, а затем уведомляет всех наблюдателей, вызывая ихupdatemethod с именем акции и новой ценой.Investors.java: Create два разных типа инвесторов, которые Implement интерфейсObserver:DayTrader— имеет приватное поле имени, задаваемое через constructor. При обновлении выводит[name] received alert: [stockName] is now $[price] - Making quick trade!LongTermInvestor— имеет приватное поле имени, задаваемое через constructor. При обновлении выводит[name] received alert: [stockName] is now $[price] - Holding steady.Format цену с двумя десятичными знаками.
Main.java: Объедините вашу систему наблюдателей! Вы получите три inputs: название акции (String), первое обновление цены (double) и второе обновление цены (double).Create
StockTickerс given названием акции. CreateDayTradernamed"Alice"иLongTermInvestornamed"Bob", а затем подпишите обоих на тикер.Установите цену в значение первого входного параметра (все наблюдатели получают уведомление). Затем отмените подписку Alice. Наконец, установите цену в значение второго входного параметра (на этот раз уведомление должен получить только Bob).
Вы получите три inputs по порядку: название акции (String), первая цена (double), вторая цена (double).
Это challenge демонстрирует возможности паттерна Observer: StockTicker ничего не знает о конкретных типах инвесторов. Он знает только, что они реализуют Observer. Когда вы отменяете подписку Alice, она перестает получать обновления, а Bob продолжает получать уведомления. Такая слабая связанность упрощает добавление новых типов инвесторов без изменения тикера!
Попробуйте сами
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 на ticker
// 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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java