Наблюдатель - определяет отношение "один ко многим" между объектами таким образом, что при изменении состояния одного объекта (Subject, Observable) происходит автоматическое оповещение и обновление всех зависимых объектов.
(Ex) JDK уже имеет реализацию паттерна через унаследование класса Observer и Observable.
Полезно когда нам нужно оповещать только по условию, а не сразу, потому что метод обновления будет действовать
только если флаг changed = true
(set/has/clear)Changed().
Большой минус то, что это уже реализованный класс. То есть extend = наследование.
-
Когда при изменении состояния одного объекта требуется что-то сделать в других, но вы не знаете наперёд какие именно объекты должны отреагировать.
-
Эта задача может возникнуть при разработке GUI фреймворка, когда надо дать возможность сторонним классам реагировать на клики по кнопкам.
-
Паттерн Наблюдатель даёт возможность любому объекту с интерфейсом подписчика, подписываться на изменения в объектах-издателях.
-
-
Когда одни объекты должны наблюдать за другими, но только в определённых случаях.
- Издатели ведут динамические списки. Все наблюдатели могут подписываться или отписываться на обновления прямо во время выполнения программы.
-
Разбейте вашу функциональность на две части: независимое ядро и опциональные зависимые части. Независимое ядро станет издателем. Зависимые части станут подписчиками.
-
Создайте интерфейс подписчиков. В большинстве случаев, в нём достаточно определить единственный метод оповещения
update()
. -
Создайте интерфейс издателей и опишите в нём операции управления подпиской. Помните, что издатель должен работать только с общим интерфейсом подписчиков.
-
(Ex) Вам нужно решить, куда поместить код ведения подписки, ведь он обычно бывает одинаков для всех типов издателей. Самый очевидный способ - вынести этот код в промежуточный абстрактный класс, от которого будут наследоваться все издатели.
-
(Ex) Но если вы интегрируете паттерн в существующие классы, то создать новый базовый класс может быть затруднительно. В этом случае, вы можете поместить логику подписки во вспомогательный объект и делегировать ему работу из издателей.
-
Создайте классы конкретных издателей. Реализуйте их так, чтобы при каждом изменении состояния, они слали оповещения всем своим подписчикам.
-
Реализуйте метод оповещения в конкретных подписчиках. Издатель может отправлять какие-то данные вместе с оповещением (например, в параметрах). Возможен и другой вариант, когда подписчик, получив оповещение, сам берёт из объекта издателя нужные данные. Но при этом подписчик привяжет себя к конкретному классу издателя. (Программировать не интерфейсами)
-
Клиент должен создавать необходимое количество объектов подписчиков и подписывать их у издателей.
+ | - |
---|---|
Издатель не зависит от конкретных классов подписчиков. | Наблюдатели оповещаются в случайном порядке. |
Вы можете подписывать и отписывать получателей на лету. | |
Реализует принцип открытости/закрытости. |
-
Цепочка обязанностей, Команда, Посредник и Наблюдатель показывают различные способы работы отправителей запросов с их получателями:
-
Цепочка обязанностей передаёт запрос последовательно через цепочку потенциальных получателей, ожидая, что какой-то из них обработает запрос.
-
Команда устанавливает косвенную одностороннюю связь от отправителей к получателям.
-
Посредник убирает прямую связь между отправителями и получателями, заставляя их общаться опосредованно, через себя.
-
Наблюдатель передаёт запрос одновременно всем заинтересованным получателям, но позволяет им динамически подписывать или отписываться от таких оповещений.
-
-
Разница между Посредником и Наблюдателем не всегда очевидна. Чаще всего они выступают как конкуренты, но иногда могут работать вместе.
Цель Посредника - убрать обоюдные зависимости между компонентами системы. Вместо этого они становятся зависимыми от самого посредника. С другой стороны, цель Наблюдателя - обеспечить динамическую одностороннюю связь, в которой одни объекты косвенно зависят от других.
Довольно популярна реализация Посредника при помощи Наблюдателя. При этом объект посредника будет выступать издателем, а все остальные компоненты станут подписчиками и смогут динамически следить за событиями, происходящими в посреднике. В этом случае трудно понять, чем же отличаются оба паттерна.
Но Посредник имеет и другие реализации, когда отдельные компоненты жёстко привязаны к объекту посредника. Такой код вряд ли будет напоминать Наблюдателя, но всё же останется Посредником.
Теперь представьте другую программу, в которой каждый компонент системы становится издателем. Компоненты могут подписываться друг на друга, в то же время, не привязываясь к конкретным классам. Программа будет состоять из целой сети наблюдателей, не имея центрального объекта посредника.