Skip to content

Latest commit

 

History

History
94 lines (58 loc) · 9.61 KB

Observer.md

File metadata and controls

94 lines (58 loc) · 9.61 KB

Наблюдатель

UML

Наблюдатель - определяет отношение "один ко многим" между объектами таким образом, что при изменении состояния одного объекта (Subject, Observable) происходит автоматическое оповещение и обновление всех зависимых объектов.

(Ex) JDK уже имеет реализацию паттерна через унаследование класса Observer и Observable. Полезно когда нам нужно оповещать только по условию, а не сразу, потому что метод обновления будет действовать только если флаг changed = true (set/has/clear)Changed().

Большой минус то, что это уже реализованный класс. То есть extend = наследование.

Применимость

  • Когда при изменении состояния одного объекта требуется что-то сделать в других, но вы не знаете наперёд какие именно объекты должны отреагировать.

    • Эта задача может возникнуть при разработке GUI фреймворка, когда надо дать возможность сторонним классам реагировать на клики по кнопкам.

    • Паттерн Наблюдатель даёт возможность любому объекту с интерфейсом подписчика, подписываться на изменения в объектах-издателях.

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

    • Издатели ведут динамические списки. Все наблюдатели могут подписываться или отписываться на обновления прямо во время выполнения программы.

Шаги реализации

  1. Разбейте вашу функциональность на две части: независимое ядро и опциональные зависимые части. Независимое ядро станет издателем. Зависимые части станут подписчиками.

  2. Создайте интерфейс подписчиков. В большинстве случаев, в нём достаточно определить единственный метод оповещения update().

  3. Создайте интерфейс издателей и опишите в нём операции управления подпиской. Помните, что издатель должен работать только с общим интерфейсом подписчиков.

  4. (Ex) Вам нужно решить, куда поместить код ведения подписки, ведь он обычно бывает одинаков для всех типов издателей. Самый очевидный способ - вынести этот код в промежуточный абстрактный класс, от которого будут наследоваться все издатели.

  5. (Ex) Но если вы интегрируете паттерн в существующие классы, то создать новый базовый класс может быть затруднительно. В этом случае, вы можете поместить логику подписки во вспомогательный объект и делегировать ему работу из издателей.

  6. Создайте классы конкретных издателей. Реализуйте их так, чтобы при каждом изменении состояния, они слали оповещения всем своим подписчикам.

  7. Реализуйте метод оповещения в конкретных подписчиках. Издатель может отправлять какие-то данные вместе с оповещением (например, в параметрах). Возможен и другой вариант, когда подписчик, получив оповещение, сам берёт из объекта издателя нужные данные. Но при этом подписчик привяжет себя к конкретному классу издателя. (Программировать не интерфейсами)

  8. Клиент должен создавать необходимое количество объектов подписчиков и подписывать их у издателей.

Преимущества и недостатки

+ -
Издатель не зависит от конкретных классов подписчиков. Наблюдатели оповещаются в случайном порядке.
Вы можете подписывать и отписывать получателей на лету.
Реализует принцип открытости/закрытости.

Отношения с другими паттернами

  • Цепочка обязанностей, Команда, Посредник и Наблюдатель показывают различные способы работы отправителей запросов с их получателями:

    • Цепочка обязанностей передаёт запрос последовательно через цепочку потенциальных получателей, ожидая, что какой-то из них обработает запрос.

    • Команда устанавливает косвенную одностороннюю связь от отправителей к получателям.

    • Посредник убирает прямую связь между отправителями и получателями, заставляя их общаться опосредованно, через себя.

    • Наблюдатель передаёт запрос одновременно всем заинтересованным получателям, но позволяет им динамически подписывать или отписываться от таких оповещений.

  • Разница между Посредником и Наблюдателем не всегда очевидна. Чаще всего они выступают как конкуренты, но иногда могут работать вместе.

    Цель Посредника - убрать обоюдные зависимости между компонентами системы. Вместо этого они становятся зависимыми от самого посредника. С другой стороны, цель Наблюдателя - обеспечить динамическую одностороннюю связь, в которой одни объекты косвенно зависят от других.

    Довольно популярна реализация Посредника при помощи Наблюдателя. При этом объект посредника будет выступать издателем, а все остальные компоненты станут подписчиками и смогут динамически следить за событиями, происходящими в посреднике. В этом случае трудно понять, чем же отличаются оба паттерна.

    Но Посредник имеет и другие реализации, когда отдельные компоненты жёстко привязаны к объекту посредника. Такой код вряд ли будет напоминать Наблюдателя, но всё же останется Посредником.

    Теперь представьте другую программу, в которой каждый компонент системы становится издателем. Компоненты могут подписываться друг на друга, в то же время, не привязываясь к конкретным классам. Программа будет состоять из целой сети наблюдателей, не имея центрального объекта посредника.