Menu
Coddy logo textTech

Паттерн Observer

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 92 из 110.

Паттерн Наблюдатель устанавливает отношение «один ко многим» между объектами: когда один объект (субъект) меняет свое состояние, все зависимые от него объекты (наблюдатели) уведомляются об этом автоматически. Это идеально подходит для таких сценариев, как обновление пользовательского интерфейса, системы событий или любые ситуации, когда несколько частей вашего приложения должны реагировать на изменения.

Этот паттерн включает в себя две ключевые роли: Субъект, который поддерживает список наблюдателей и уведомляет их об изменениях, и Наблюдатели, которые регистрируют себя и реагируют на обновления:

abstract class Observer {
  void update(String message);
}

class NewsAgency {
  final List<Observer> _observers = [];
  String _latestNews = '';

  void subscribe(Observer observer) {
    _observers.add(observer);
  }

  void unsubscribe(Observer observer) {
    _observers.remove(observer);
  }

  void publishNews(String news) {
    _latestNews = news;
    for (var observer in _observers) {
      observer.update(_latestNews);
    }
  }
}

class NewsChannel implements Observer {
  final String name;
  NewsChannel(this.name);

  @override
  void update(String message) {
    print('$name received: $message');
  }
}

void main() {
  var agency = NewsAgency();
  var cnn = NewsChannel('CNN');
  var bbc = NewsChannel('BBC');

  agency.subscribe(cnn);
  agency.subscribe(bbc);

  agency.publishNews('Breaking news!');
  // CNN получил: Breaking news!
  // BBC получил: Breaking news!
}

Когда вызывается publishNews, агентство перебирает всех подписанных наблюдателей и вызывает их метод update. Наблюдатели могут подписываться или отписываться в любое время, что делает систему гибкой и слабосвязанной — субъекту не нужно знать конкретные типы своих наблюдателей.

challenge icon

Задание

Легко

Давайте создадим систему оповещения о ценах на акции, используя паттерн Наблюдатель (Observer)! Вы создадите систему, в которой биржевой тикер уведомляет нескольких инвесторов при каждом изменении цены, позволяя каждому инвестору реагировать на обновления рынка по-своему.

Вы организуете свой код в двух файлах:

  • stock_system.dart: Создайте здесь свою систему наблюдателей:
    • Абстрактный класс Investor с методом void onPriceChange(String stockName, double price)
    • Класс DayTrader, который реализует Investor. У него есть поле name, а его метод onPriceChange выводит: [name] sees [stockName] at $[price] - Making quick trade!
    • Класс LongTermInvestor, который реализует Investor. У него есть поле name, а его метод onPriceChange выводит: [name] notes [stockName] at $[price] - Holding steady.
    • Класс StockTicker (субъект) с:
      • Полем String stockName
      • Приватным списком для хранения подписанных инвесторов
      • Методом subscribe(Investor investor) для добавления инвестора
      • Методом unsubscribe(Investor investor) для удаления инвестора
      • Методом updatePrice(double newPrice), который уведомляет всех подписанных инвесторов о новой цене
  • main.dart: Импортируйте вашу систему акций и продемонстрируйте паттерн Наблюдатель в действии:
    • Создайте StockTicker для акции с именем DART
    • Создайте DayTrader по имени Alice и LongTermInvestor по имени Bob
    • Подпишите обоих инвесторов на тикер
    • Обновите цену до 150.50 (оба инвестора должны быть уведомлены)
    • Выведите пустую строку
    • Отпишите Alice от тикера
    • Обновите цену до 155.75 (теперь должен быть уведомлен только Bob)

Это демонстрирует, как паттерн Наблюдатель создает слабосвязанную систему — StockTicker не нужно знать, уведомляет ли он дневных трейдеров или долгосрочных инвесторов, он просто вызывает onPriceChange у всех, кто подписан!

Ожидаемый вывод:

Alice sees DART at $150.5 - Making quick trade!
Bob notes DART at $150.5 - Holding steady.

Bob notes DART at $155.75 - Holding steady.

Шпаргалка

Паттерн Наблюдатель (Observer) устанавливает связь «один ко многим», при которой субъект уведомляет всех своих наблюдателей об изменении своего состояния.

У этого паттерна есть две ключевые роли:

  • Subject (Субъект): хранит список наблюдателей и уведомляет их об изменениях
  • Observer (Наблюдатель): регистрируется у субъекта и реагирует на обновления

Базовая реализация:

abstract class Observer {
  void update(String message);
}

class Subject {
  final List<Observer> _observers = [];

  void subscribe(Observer observer) {
    _observers.add(observer);
  }

  void unsubscribe(Observer observer) {
    _observers.remove(observer);
  }

  void notifyObservers(String data) {
    for (var observer in _observers) {
      observer.update(data);
    }
  }
}

class ConcreteObserver implements Observer {
  final String name;
  ConcreteObserver(this.name);

  @override
  void update(String message) {
    print('$name received: $message');
  }
}

Основные преимущества:

  • Слабая связанность между субъектом и наблюдателями
  • Наблюдатели могут подписываться и отписываться динамически
  • Субъекту не нужно знать конкретные типы наблюдателей

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

import 'stock_system.dart';

void main() {
  // TODO: Создайте StockTicker для акции с названием 'DART'
  
  // TODO: Создайте DayTrader по имени 'Alice'
  
  // TODO: Создайте LongTermInvestor по имени 'Bob'
  
  // TODO: Подпишите обоих инвесторов на ticker
  
  // TODO: Обновите цену до 150.50 (оба инвестора должны получить уведомление)
  
  // TODO: Выведите пустую строку
  
  // TODO: Отпишите Alice от ticker
  
  // TODO: Обновите цену до 155.75 (теперь уведомление должен получить только Bob)
}
quiz iconПроверьте себя

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

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