Шаблон наблюдателя является одним из шаблонов поведенческого проектирования . Шаблон проектирования 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 messageMessage Posted to Topic:New MessageObj1:: Consuming message::New MessageObj2:: Consuming message::New MessageObj3:: Consuming message::New Message |
Диаграмма Класса Наблюдателя
Шаблон наблюдателя также называется шаблоном публикации-подписки. Некоторые из его реализаций;
- java.util.EventListener в Swing
- javax.servlet.http.HttpSessionBindingListener
- javax.servlet.http.HttpSessionAttributeListener
Это все для шаблона Observer в Java, надеюсь, вам понравилось. Поделитесь своей любовью с комментариями и поделитесь ею с другими.
