Шаблон наблюдателя является одним из шаблонов поведенческого проектирования . Шаблон проектирования Observer полезен, когда вы интересуетесь состоянием объекта и хотите получать уведомления при любых изменениях. В шаблоне наблюдателя объект, который наблюдает за состоянием другого объекта, называется наблюдателем, а объект, который отслеживается, называется субъектом . Согласно GoF, намерение наблюдателя — это;
Определите зависимость «один ко многим» между объектами, чтобы при изменении состояния одного объекта все его иждивенцы уведомлялись и обновлялись автоматически.
Субъект содержит список наблюдателей, которые уведомляют о любых изменениях своего состояния, поэтому он должен предоставить методы, с помощью которых наблюдатели могут регистрироваться и отменять регистрацию. Субъект также содержит метод для уведомления всех наблюдателей о любых изменениях и может либо отправить обновление при уведомлении наблюдателя, либо может предоставить другой метод для получения обновления.
У Обозревателя должен быть метод для установки объекта для наблюдения и другой метод, который будет использоваться Субъектом для уведомления их о любых обновлениях.
Java предоставляет встроенную платформу для реализации шаблона Observer через класс java.util.Observable и интерфейс java.util.Observer . Однако он не очень широко используется, потому что реализация действительно проста, и в большинстве случаев мы не хотим заканчивать расширением класса только для реализации шаблона Observer, поскольку java не обеспечивает множественное наследование в классах.
Служба сообщений Java (JMS) использует шаблон Observer вместе с шаблоном Mediator, чтобы приложения могли подписываться и публиковать данные в других приложениях.
Платформы Model-View-Controller (MVC) также используют шаблон Observer, где Model — это субъект, а Views — наблюдатели, которые могут зарегистрироваться, чтобы получать уведомления о любых изменениях в модели.
Пример Наблюдателя
В нашем примере мы реализуем простую тему, и наблюдатели могут зарегистрироваться в этой теме. Всякий раз, когда любое новое сообщение будет опубликовано в теме, все наблюдатели реестров будут уведомлены, и они могут использовать сообщение.
Основываясь на требованиях субъекта, здесь представлен базовый интерфейс субъекта, который определяет методы контракта, которые должны быть реализованы любым конкретным субъектом.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
package com.journaldev.design.observer; public interface Subject { //methods to register and unregister observers public void register(Observer obj); public void unregister(Observer obj); //method to notify observers of change public void notifyObservers(); //method to get updates from subject public Object getUpdate(Observer obj); } |
Далее мы создадим контракт для Обозревателя, будет метод для прикрепления Субъекта к наблюдателю и другой метод, который Субъект будет использовать для уведомления о любых изменениях.
01
02
03
04
05
06
07
08
09
10
|
package com.journaldev.design.observer; public interface Observer { //method to update the observer, used by subject public void update(); //attach with subject to observe public void setSubject(Subject sub); } |
Теперь наш контракт готов, давайте приступим к конкретной реализации нашей темы.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
package com.journaldev.design.observer; import java.util.ArrayList; import java.util.List; public class MyTopic implements Subject { private List<Observer> observers; private String message; private boolean changed; private final Object MUTEX= new Object(); public MyTopic(){ this .observers= new ArrayList<>(); } @Override public void register(Observer obj) { if (obj == null ) throw new NullPointerException( "Null Observer" ); if (!observers.contains(obj)) observers.add(obj); } @Override public void unregister(Observer obj) { observers.remove(obj); } @Override public void notifyObservers() { List<Observer> observersLocal = null ; //synchronization is used to make sure any observer registered after message is received is not notified synchronized (MUTEX) { if (!changed) return ; observersLocal = new ArrayList<>( this .observers); this .changed= false ; } for (Observer obj : observersLocal) { obj.update(); } } @Override public Object getUpdate(Observer obj) { return this .message; } //method to post message to the topic public void postMessage(String msg){ System.out.println( "Message Posted to Topic:" +msg); this .message=msg; this .changed= true ; notifyObservers(); } } |
Реализация метода для регистрации и отмены регистрации наблюдателя очень проста, дополнительный метод — postMessage (), который будет использоваться клиентским приложением для публикации сообщения String в теме. Обратите внимание на логическую переменную, которая отслеживает изменение состояния темы и используется при уведомлении наблюдателей. Эта переменная необходима для того, чтобы в случае отсутствия обновления и вызова метода notifyObservers () наблюдатели не отправляли ложные уведомления.
Также обратите внимание на использование синхронизации в методе notifyObservers (), чтобы уведомление отправлялось только зарегистрированным наблюдателям до публикации сообщения в теме.
Вот реализация Observers, которая будет следить за предметом.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package com.journaldev.design.observer; public class MyTopicSubscriber implements Observer { private String name; private Subject topic; public MyTopicSubscriber(String nm){ this .name=nm; } @Override public void update() { String msg = (String) topic.getUpdate( this ); if (msg == null ){ System.out.println(name+ ":: No new message" ); } else System.out.println(name+ ":: Consuming message::" +msg); } @Override public void setSubject(Subject sub) { this .topic=sub; } } |
Обратите внимание на реализацию метода update (), где он вызывает метод Subject getUpdate (), чтобы получить сообщение для потребления. Мы могли бы избежать этого вызова, передав сообщение в качестве аргумента методу update () .
Вот простая тестовая программа для использования нашей тематической реализации.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.journaldev.design.observer; public class ObserverPatternTest { public static void main(String[] args) { //create subject MyTopic topic = new MyTopic(); //create observers Observer obj1 = new MyTopicSubscriber( "Obj1" ); Observer obj2 = new MyTopicSubscriber( "Obj2" ); Observer obj3 = new MyTopicSubscriber( "Obj3" ); //register observers to the subject topic.register(obj1); topic.register(obj2); topic.register(obj3); //attach observer to subject obj1.setSubject(topic); obj2.setSubject(topic); obj3.setSubject(topic); //check if any update is available obj1.update(); //now send message to subject topic.postMessage( "New Message" ); } } |
Когда мы запускаем вышеуказанную программу, мы получаем следующий вывод.
1
2
3
4
5
|
Obj1:: No new message Message Posted to Topic:New Message Obj1:: Consuming message::New Message Obj2:: Consuming message::New Message Obj3:: Consuming message::New Message |
Диаграмма Класса Наблюдателя
Шаблон наблюдателя также называется шаблоном публикации-подписки. Некоторые из его реализаций;
- java.util.EventListener в Swing
- javax.servlet.http.HttpSessionBindingListener
- javax.servlet.http.HttpSessionAttributeListener
Это все для шаблона Observer в Java, надеюсь, вам понравилось. Поделитесь своей любовью с комментариями и поделитесь ею с другими.