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 продолжает получать уведомления. Такая слабая связанность позволяет легко добавлять новые типы инвесторов без изменения тикера!

Шпаргалка

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

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

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