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与模式》之观察者模式

原文地址:https://www.cnblogs.com/WJQ2017/p/7746416.html