Патерн (шаблон) проектування observer (спостерігач)

Призначення паттерна Observer

  • Патерн Observer визначає залежність "один-ко-многим" між об'єктами так, що при зміні стану одного об'єкта всіх залежних від нього об'єкти повідомляються і оновлюються автоматично.
  • Патерн Observer инкапсулирует головний (незалежний) компонент в абстракцію Subject і змінювані (залежні) компоненти в ієрархію Observer.
  • Патерн Observer визначає частина "View" в моделі Model-View-Controller (MVC).

Патерн Observer знаходить широке застосування в системах призначеного для користувача інтерфейсу, в яких дані і їх уявлення ( "види") відокремлені один від одного. При зміні даних повинні бути змінені всі подання цих даних (наприклад, у вигляді таблиці, графіка та діаграми).

розв'язувана проблема

Є система, що складається з безлічі взаємодіючих класів. При цьому взаємодіючі об'єкти повинні знаходитися в узгоджених станах. Ви хочете уникнути монолітності такої системи, зробивши класи слабо пов'язаними (або повторно використовуваними).

Обговорення паттерна Observer

Патерн Observer визначає об'єкт Subject, який зберігає дані (модель), а всю функціональність "уявлень" делегує слабо зв'язаної окремих об'єктах Observer. При створенні спостерігачі Observer реєструються в об'єкта Subject. Коли об'єкт Subject змінюється, він сповіщає про це всіх зареєстрованих спостерігачів. Після цього кожен оглядач запитує у об'єкта Subject ту частину статків, яка необхідна для відображення даних.

Така схема дозволяє динамічно налаштовувати кількість і "типи" уявлень об'єктів.

Описаний вище протокол взаємодії відповідає моделі витягування (pull), коли суб'єкт інформує спостерігачів про своє зміні, і кожен спостерігач відповідальний за "витягування" у Subject потрібних йому даних. Існує також модель проштовхування, коли суб'єкт Subject посилає ( "проштовхує") спостерігачам детальну інформацію про своє зміні.

Існує також ряд питань, про які слід згадати, але обговорення яких залишиться за рамками даної статті:

  • Реалізація "компресії" повідомлень (посилка єдиного сповіщення на серію послідовних змін суб'єкта Subject).
  • Моніторинг декількох суб'єктів за допомогою одного спостерігача Observer.
  • Виняток висячих посилань у спостерігачів на віддалені суб'єкти. Для цього суб'єкт повинен повідомити спостерігачів про своє видаленні.

Патерн Observer вперше був застосований в архітектурі Model-View-Controller мови Smalltalk, що представляє каркас для побудови призначених для користувача інтерфейсів.

Структура паттерна Observer

Subject представляє головну (незалежну) абстракцію. Observer являє змінну (залежну) абстракцію. Суб'єкт сповіщає спостерігачів про своє зміні, на що кожен спостерігач може запросити стан суб'єкта.

UML-діаграма класів паттерна Observer

Приклад паттерна Observer

Патерн Observer визначає залежність "один-ко-многим" між об'єктами так, що при зміні стану одного об'єкта всіх залежних від нього об'єкти повідомляються і оновлюються автоматично. Деякі аукціони демонструють цей патерн. Кожен учасник має картку з цифрами, яку він використовує для позначення пропонованої ціни (ставки). Ведучий аукціону (Subject) починає торги і спостерігає, коли хто-небудь піднімає картку, пропонуючи нову більш високу ціну. Ведучий приймає заявку, про що тут же сповіщає всіх учасників аукціону (Observers).

Використання паттерна Observer

  1. Проведіть відмінності між основною (або незалежної) і додаткової (або залежною) функціональностями.
  2. Смоделируйте "незалежну" функціональність за допомогою абстракції "суб'єкт".
  3. Смоделируйте "залежну" функціональність за допомогою ієрархії "спостерігач".
  4. Клас Subject пов'язаний тільки c базовим класом Observer.
  5. Клієнт налаштовує кількість і типи спостерігачів.
  6. Спостерігачі реєструються у суб'єкта.
  7. Суб'єкт сповіщає всіх зареєстрованих спостерігачів.
  8. Суб'єкт може "проштовхнути" інформацію в спостерігачів, або спостерігачі можуть "витягнути" необхідну їм інформацію від об'єкта Subject.

Особливості паттерна Observer

  • Патерни Chain of Responsibility. Command. Mediator і Observer показують, як можна розділити відправників і одержувачів запитів з урахуванням своїх особливостей. Chain of Responsibility передає запит відправника по ланцюжку потенційних одержувачів. Command визначає зв'язок - "оправітель-одержувач" за допомогою підкласу. У Mediator відправник і одержувач посилаються один на одного побічно, через об'єкт-посередник. У паттерне Observer зв'язок між відправником і отримувачем виходить слабкою, при цьому число одержувачів може конфигурироваться під час виконання.
  • Mediator і Observer є конкуруючими паттернами. Якщо Observer розподіляє взаємодія c допомогою об'єктів "спостерігач" і "суб'єкт", то Mediator використовує об'єкт-посередник для інкапсуляції взаємодії між іншими об'єктами. Ми виявили, що легше зробити повторно використовуваними Спостерігачів і Суб'єктів, ніж Посередників.
  • Mediator може використовувати Observer для динамічної реєстрації колег і їх взаємодії з посередником.

Реалізація патерну Observer

Реалізація патерну Observer по кроках

  1. Смоделируйте "незалежну" функціональність за допомогою абстракції "суб'єкт".
  2. Смоделируйте "залежну" функціональність за допомогою ієрархії "спостерігач".
  3. Клас Subject пов'язаний тільки c базовим класом Observer.
  4. Спостерігачі реєструються у суб'єкта.
  5. Суб'єкт сповіщає всіх зареєстрованих спостерігачів.
  6. Спостерігачі "витягають" необхідну їм інформацію від об'єкта Subject.
  7. Клієнт налаштовує кількість і типи спостерігачів.

Реалізація патерну Observer: до і після