Menu
Coddy logo textTech

Паттерн 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. Это позволяет легко добавлять новые типы наблюдателей без изменения кода субъекта.

challenge icon

Задание

Легко

Давайте создадим систему оповещения о ценах на акции, используя паттерн Наблюдатель (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 продолжает получать уведомления. Такая слабая связанность позволяет легко добавлять новые типы инвесторов без изменения тикера!

Попробуйте сами

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();
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование