Паттерн 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. Наблюдатели могут подписываться или отписываться в любое время, что делает систему гибкой и слабосвязанной — субъекту не нужно знать конкретные типы своих наблюдателей.
Задание
ЛегкоДавайте создадим систему оповещения о ценах на акции, используя паттерн Наблюдатель (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)
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий