Java 设计模式 观察者模式
Posted 王景迁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 设计模式 观察者模式相关的知识,希望对你有一定的参考价值。
在阎宏博士的《JAVA与模式》一书中开头是这样描述观察者(Observer)模式的:观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式的结构
一个软件系统通常要求在某个对象的状态变化时,其他的某些对象随之变化。观察者模式是满足这一要求的所有方案中最好的一种。结构图如下:
涉及的角色:
抽象主题(Subject)角色:抽象主题角色又叫做抽象被观察者(Observable)角色。定义一个可以增加和删除观察者对象的接口,每个主题把所有观察者对象的引用保存在一个集合(例如ArrayList)里面。
具体主题(ConcreteSubject)角色:具体主题角色又叫做具体被观察者(Concrete Observable)角色。在具体主题的状态变化时,给所有登记过的观察者发出通知。
抽象观察者(Observer)角色:为所有的具体观察者定义一个更新接口,收到主题的通知时更新具体观察者。
具体观察者(ConcreteObserver)角色:存储与主题状态适应的状态,实现了抽象观察者角色的更新接口。
抽象主题角色类
1 public abstract class Subject {
2 /**
3 * 用来保存注册的观察者对象
4 */
5 private List<Observer> list = new ArrayList<Observer>();
6 /**
7 * 注册观察者对象
8 * @param observer 观察者对象
9 */
10 public void attach(Observer observer){
11
12 list.add(observer);
13 System.out.println("Attached an observer");
14 }
15 /**
16 * 删除观察者对象
17 * @param observer 观察者对象
18 */
19 public void detach(Observer observer){
20
21 list.remove(observer);
22 }
23 /**
24 * 通知所有注册的观察者对象
25 */
26 public void nodifyObservers(String newState){
27
28 for(Observer observer : list){
29 observer.update(newState);
30 }
31 }
32 }
具体主题角色类
1 public class ConcreteSubject extends Subject{
2
3 private String state;
4
5 public String getState() {
6 return state;
7 }
8
9 public void change(String newState){
10 state = newState;
11 System.out.println("主题状态为:" + state);
12 //状态发生改变,通知各个观察者
13 this.nodifyObservers(state);
14 }
15 }
抽象观察者角色类
1 public interface Observer {
2 /**
3 * 更新接口
4 * @param state 更新的状态
5 */
6 public void update(String state);
7 }
具体观察者角色类
1 public class ConcreteObserver implements Observer {
2 //观察者的状态
3 private String observerState;
4
5 @Override
6 public void update(String state) {
7 /**
8 * 更新观察者的状态,使其与目标的状态保持一致
9 */
10 observerState = state;
11 System.out.println("状态为:"+observerState);
12 }
13
14 }
客户端类
1 public class Client {
2
3 public static void main(String[] args) {
4 //创建主题对象
5 ConcreteSubject subject = new ConcreteSubject();
6 //创建观察者对象
7 Observer observer = new ConcreteObserver();
8 //将观察者对象登记到主题对象上
9 subject.attach(observer);
10 //改变主题对象的状态
11 subject.change("new state");
12 }
13
14 }
结果:
客户端先创建具体主题类的实例和一个观察者对象。然后,它调用主题对象的attach方法,将观察者对象登记到主题对象上,即把它加入到主题对象的集合中。客户端调用主题对象的change方法后改变了主题对象的状态。主题对象在状态变化时,调用父类的notifyObservers方法,通知所有登记过的观察者对象,更新它们的状态。
推模型和拉模型
观察者模式分为推模型和拉模型两种方式。
推模型:不管观察者对象是否需要,主题对象都会向观察者对象推送主题的详细信息。
拉模型: 主题对象在通知观察者对象时,只传递少量信息。如果观察者对象需要更具体的信息,观察者对象需要主动向主题对象获取,相当于观察者对象从主题对象中拉数据。通过update方法把主题对象传递给观察者对象,通过主题对象获取信息。
前面的例子是推模型,下面给出一个拉模型的实例:
拉模型的抽象观察者类
1 public interface Observer {
2 /**
3 * 更新接口
4 * @param subject 传入主题对象,方面获取相应的主题对象的状态
5 */
6 public void update(Subject subject);
7 }
拉模型的具体观察者类
1 public class ConcreteObserver implements Observer {
2 //观察者的状态
3 private String observerState;
4
5 @Override
6 public void update(Subject subject) {
7 /**
8 * 更新观察者的状态,使其与目标的状态保持一致
9 */
10 observerState = ((ConcreteSubject)subject).getState();
11 System.out.println("观察者状态为:"+observerState);
12 }
13
14 }
拉模型的抽象主题类
1 public abstract class Subject {
2 /**
3 * 用来保存注册的观察者对象
4 */
5 private List<Observer> list = new ArrayList<Observer>();
6 /**
7 * 注册观察者对象
8 * @param observer 观察者对象
9 */
10 public void attach(Observer observer){
11
12 list.add(observer);
13 System.out.println("Attached an observer");
14 }
15 /**
16 * 删除观察者对象
17 * @param observer 观察者对象
18 */
19 public void detach(Observer observer){
20
21 list.remove(observer);
22 }
23 /**
24 * 通知所有注册的观察者对象
25 */
26 public void nodifyObservers(){
27
28 for(Observer observer : list){
29 observer.update(this);
30 }
31 }
32 }
拉模型的具体主题类
1 public class ConcreteSubject extends Subject{
2
3 private String state;
4
5 public String getState() {
6 return state;
7 }
8
9 public void change(String newState){
10 state = newState;
11 System.out.println("主题状态为:" + state);
12 //状态发生改变,通知各个观察者
13 this.nodifyObservers();
14 }
15 }
两种模式的比较
推模型是假设主题对象知道观察者对象需要的数据;而拉模型是主题对象不知道观察者对象需要什么数据。
推模型可能会使观察者对象难以复用,因为观察者对象的update方法是按需要定义形参,可能无法兼顾没有考虑到的使用情况。当新情况出现时,就可能需要提供新的update方法,或者重新实现观察者类;而拉模型不会出现这种情况,因为观察者对象的update方法的形参是主题对象,通过主题对象可以获取所有信息。
Java对观察者模式的支持
java.util库提供了一个Observer接口和一个Observable类。
Observer接口
只定义了一个update方法。当被观察者对象的状态变化时,被观察者对象的notifyObservers方法就会调用该方法。
1 public interface Observer {
2 void update(Observable o, Object arg);
3 }
Observable类
被观察者类相当于抽象主题类,是java.util.Observable类的子类。有两个方法对Observable的子类来说很重要:一个是setChanged方法,被调用后会设置一个标记变量,代表被观察者对象的状态变化了。第二个是notifyObservers方法,被调用时会调用所有登记过的观察者对象的update方法,更新观察者对象。
1 public class Observable {
2 private boolean changed = false;
3 private Vector obs;
4
5 /** Construct an Observable with zero Observers. */
6
7 public Observable() {
8 obs = new Vector();
9 }
10
11 /**
12 * 将一个观察者添加到观察者聚集上面
13 */
14 public synchronized void addObserver(Observer o) {
15 if (o == null)
16 throw new NullPointerException();
17 if (!obs.contains(o)) {
18 obs.addElement(o);
19 }
20 }
21
22 /**
23 * 将一个观察者从观察者聚集上删除
24 */
25 public synchronized void deleteObserver(Observer o) {
26 obs.removeElement(o);
27 }
28
29 public void notifyObservers() {
30 notifyObservers(null);
31 }
32
33 /**
34 * 如果本对象有变化(那时hasChanged 方法会返回true)
35 * 调用本方法通知所有登记的观察者,即调用它们的update()方法
36 * 传入this和arg作为参数
37 */
38 public void notifyObservers(Object arg) {
39
40 Object[] arrLocal;
41
42 synchronized (this) {
43
44 if (!changed)
45 return;
46 arrLocal = obs.toArray();
47 clearChanged();
48 }
49
50 for (int i = arrLocal.length-1; i>=0; i--)
51 ((Observer)arrLocal[i]).update(this, arg);
52 }
53
54 /**
55 * 将观察者聚集清空
56 */
57 public synchronized void deleteObservers() {
58 obs.removeAllElements();
59 }
60
61 /**
62 * 将“已变化”设置为true
63 */
64 protected synchronized void setChanged() {
65 changed = true;
66 }
67
68 /**
69 * 将“已变化”重置为false
70 */
71 protected synchronized void clearChanged() {
72 changed = false;
73 }
74
75 /**
76 * 检测本对象是否已变化
77 */
78 public synchronized boolean hasChanged() {
79 return changed;
80 }
81
82 /**
83 * Returns the number of observers of this <tt>Observable</tt> object.
84 *
85 * @return the number of observers of this object.
86 */
87 public synchronized int countObservers() {
88 return obs.size();
89 }
90 }
如何使用?
被观察者类Watched
1 public class Watched extends Observable{
2
3 private String data = "";
4
5 public String getData() {
6 return data;
7 }
8
9 public void setData(String data) {
10
11 if(!this.data.equals(data)){
12 this.data = data;
13 setChanged();
14 }
15 notifyObservers();
16 }
17
18
19 }
观察者类Watcher
1 public class Watcher implements Observer{
2
3 public Watcher(Observable o){
4 o.addObserver(this);
5 }
6
7 @Override
8 public void update(Observable o, Object arg) {
9
10 System.out.println("状态发生改变:" + ((Watched)o).getData());
11 }
12
13 }
测试类Test
public class Test {
public static void main(String[] args) {
//创建被观察者对象
Watched watched = new Watched();
//创建观察者对象,并将被观察者对象登记
Observer watcher = new Watcher(watched);
//给被观察者状态赋值
watched.setData("start");
watched.setData("run");
watched.setData("stop");
}
}
先创建Watched和Watcher对象。在创建Watcher对象时,将Watched对象作为参数传入。然后Watched对象调用setData方法,更改Watched对象的状态。Watched对象通知登记过的Watcher对象,调用它的update方法。
参考资料
以上是关于Java 设计模式 观察者模式的主要内容,如果未能解决你的问题,请参考以下文章