观察者模式是一种行为设计模式。观察者模式的用途是,当你对一个对象的状态感兴趣,希望在它每次发生变化时获得通知。在观察者模式中,观察另外一个对象状态的对象叫做Observer观察者,被观察的对象叫着Subject被观察者。根据GoF规则,观察者模式的意图是:
定义对象之间一对多的依赖关系,一个对象状态改变,其他相关联的对象就会得到通知并被自动更新。
Subject(被观察者)包含了一些需要在其状态改变时通知的观察者。因此,他应该提供给观察者可以register(注册)自己和unregister(注销)自己的方法。当Subject(被观察者)发生变化的时候,也需要包含一个方法来通知所有观察者。当通知观察者的时候,可以推送更新内容,或者提供另外一个方法来获得更新内容。
观察者应该有一种方法,这种方法能够设置观察者对象并且可以由被观察者使用来通知其更新。
JAVA提供了内置的方式来实现观察者模式,java.util.Observable和java.util.Observer接口。然而他们用的不是很广泛。因为此实现过于简单,大多数时候我们都不想最后扩展的类仅仅是实现了观察者模式,因为JAVA类不能多继承。
Java Messages Service(JMS)消息服务使用观察者模式与命令模式来实现不同的程序之间的数据的发布和订阅。
MVC模型-视图-控制框架也使用观察者模式,把模型当做被观察者,视图视为观察者。视图能够注册自己到模型上来获得模型的改变。
观察者模式例子
在此例中,我们将完成一个简单的主题讨论,观察者能够注册此主题。任何在此主题上的内容提交导致的变化都会通知所有在注册的观察者。
基于Subject被观察者的需求,这个是实现一个基本的Subject接口,此接口定了一系列具体的方法需要在随后实现接口的具体类中被实现。
01 |
package com.journaldev.design.observer; |
02 |
03 |
public interface Subject { |
04 |
05 |
//methods to register and unregister observers |
06 |
public void register(Observer obj); |
07 |
public void unregister(Observer obj); |
08 |
09 |
//method to notify observers of change |
10 |
public void notifyObservers(); |
11 |
12 |
//method to get updates from subject |
13 |
public Object getUpdate(Observer obj); |
14 |
15 |
} |
现在创建一个相关联的观察者。它需要有一个方法能使Subject附属于一个观察者。另外的方法能够接受Subject的变化通知。
01 |
package com.journaldev.design.observer; |
02 |
03 |
public interface Observer { |
04 |
05 |
//method to update the observer, used by subject |
06 |
public void update(); |
07 |
08 |
//attach with subject to observe |
09 |
public void setSubject(Subject sub); |
10 |
} |
这种关联已经建立。现在实现具体的主题。
01 |
package com.journaldev.design.observer; |
02 |
03 |
import java.util.ArrayList; |
04 |
import java.util.List; |
05 |
06 |
public class MyTopic implements Subject { |
07 |
08 |
private List<Observer> observers; |
09 |
private String message; |
10 |
private boolean changed; |
11 |
private final Object MUTEX= new Object(); |
12 |
13 |
public MyTopic(){ |
14 |
this .observers= new ArrayList<>(); |
15 |
} |
16 |
@Override |
17 |
public void register(Observer obj) { |
18 |
if (obj == null ) throw new NullPointerException( "Null Observer" ); |
19 |
if (!observers.contains(obj)) observers.add(obj); |
20 |
} |
21 |
22 |
@Override |
23 |
public void unregister(Observer obj) { |
24 |
observers.remove(obj); |
25 |
} |
26 |
27 |
@Override |
28 |
public void notifyObservers() { |
29 |
List<Observer> observersLocal = null ; |
30 |
//synchronization is used to make sure any observer registered after message is received is not notified |
31 |
synchronized (MUTEX) { |
32 |
if (!changed) |
33 |
return ; |
34 |
observersLocal = new ArrayList<>( this .observers); |
35 |
this .changed= false ; |
36 |
} |
37 |
for (Observer obj : observersLocal) { |
38 |
obj.update(); |
39 |
} |
40 |
41 |
} |
42 |
43 |
@Override |
44 |
public Object getUpdate(Observer obj) { |
45 |
return this .message; |
46 |
} |
47 |
48 |
//method to post message to the topic |
49 |
public void postMessage(String msg){ |
50 |
System.out.println( "Message Posted to Topic:" +msg); |
51 |
this .message=msg; |
52 |
this .changed= true ; |
53 |
notifyObservers(); |
54 |
} |
55 |
56 |
} |
注册与注销观察者方法的实现非常简单,额外的方法postMessage()将被客户端应用来提交一个字符串消息给此主题。注意,布尔变量用于追踪主题状态的变化并且通知观察者此种变化。这个变量是必须的,因为如果没有更新,但是有人调用notifyObservers()方法,他就不能发送错误的通知信息给观察者。
此外需要注意的是,notifyObservers()中使用synchronization同步的方式来确保在消息被发布给主题之前,通知只能被发送到注册的观察者处。
此处是观察者的实现。他们将一直关注subject对象。
01 |
package com.journaldev.design.observer; |
02 |
03 |
public class MyTopicSubscriber implements Observer { |
04 |
05 |
private String name; |
06 |
private Subject topic; |
07 |
08 |
public MyTopicSubscriber(String nm){ |
09 |
this .name=nm; |
10 |
} |
11 |
@Override |
12 |
public void update() { |
13 |
String msg = (String) topic.getUpdate( this ); |
14 |
if (msg == null ){ |
15 |
System.out.println(name+ ":: No new message" ); |
16 |
} else |
17 |
System.out.println(name+ ":: Consuming message::" +msg); |
18 |
} |
19 |
20 |
@Override |
21 |
public void setSubject(Subject sub) { |
22 |
this .topic=sub; |
23 |
} |
24 |
25 |
} |
注意,update()方法的实现使用了被观察者的getUpdate()来处理更新的消息。此处应该避免把消息作为参数传递给update()方法。
一下为简单地测试程序来验证话题类的实现。
01 |
package com.journaldev.design.observer; |
02 |
03 |
public class ObserverPatternTest { |
04 |
05 |
public static void main(String[] args) { |
06 |
//create subject |
07 |
MyTopic topic = new MyTopic(); |
08 |
09 |
//create observers |
10 |
Observer obj1 = new MyTopicSubscriber( "Obj1" ); |
11 |
Observer obj2 = new MyTopicSubscriber( "Obj2" ); |
12 |
Observer obj3 = new MyTopicSubscriber( "Obj3" ); |
13 |
14 |
//register observers to the subject |
15 |
topic.register(obj1); |
16 |
topic.register(obj2); |
17 |
topic.register(obj3); |
18 |
19 |
//attach observer to subject |
20 |
obj1.setSubject(topic); |
21 |
obj2.setSubject(topic); |
22 |
obj3.setSubject(topic); |
23 |
24 |
//check if any update is available |
25 |
obj1.update(); |
26 |
27 |
//now send message to subject |
28 |
topic.postMessage( "New Message" ); |
29 |
} |
30 |
31 |
} |
此处为上述输出内容:
1 |
Obj1:: No new message |
2 |
Message Posted to Topic:New Message |
3 |
Obj1:: Consuming message::New Message |
4 |
Obj2:: Consuming message::New Message |
5 |
Obj3:: Consuming message::New Message</pre> |
观察者模式的UML图
观察者模式也被叫做发布订阅模式。JAVA中的一些具体应用如下:
- Swing 中的 java.util.EventListener
- javax.servlet.http.HttpSessionBindingListener
- javax.servlet.http.HttpSessionAttributeListener
以上为全部的观察者模式。希望你已经喜欢上它了。在评论中分享你的感受或者请分享给其他人。