观察者模式

来源:互联网 发布:阿里云 贴吧签到 编辑:程序博客网 时间:2024/06/03 23:26

模式定义

我们知道面向对象的一个主题就是类实例的实体化,即每个实体都有自己的状态和行为,而仿照真实世界的不同客观实体之间是存在诸多联系的,则一个很常见的对象联动方式就是反馈行为的发生。最常见的场景如:班车进站,众旅客上车,这是一个典型的对象反馈行为。由此产生的一种描述该现象的设计模式,称之为观察者模式,也有其他的名字如发布-订阅模式、模型-视图模式等。

观察者模式作为一种对象行为型模式,用于监控对象状态,并根据对象状态的改变而通知与其存在依赖关系的其他对象,所谓的“通知”其实就是产生反馈效果,所以该模式通常应用于一对一和一对多的对象关系中。

模式示例

以上面提到的班车进站和乘客上车为例,结构如下:


1、Observer作为观察者接口,只有一个update方法,作为抽象的响应函数,重点是其参数

2、passenger作为具体观察者,即等待车辆的乘客,实现update函数,即上车动作,这里的参数是观察目标,即车辆的编号(标志车辆)

3、Observable作为观察目标类,包含一个对观察者的集合,以实现在自身状态发生改变时,完成“通知”功能

4、Bus继承Observable类,也就是具体的目标类,这里模仿的是汽车进站场景,所以自身提供了一个进站函数(覆盖空的父类change函数),如果是汽车的其他场景,则需要提供其他类型的函数,作为改变状态,产生反馈源信息的条件。

示例代码

public class t{public static void main(String[] args){Observer pass1=new passenger("张三");//构造三个观察者Observer pass2=new passenger("李四");Observer pass3=new passenger("王五");Observable car=new Bus("916-1号线");//一个观察目标car.add(pass1);//添加观察关系car.add(pass2);car.add(pass3);car.change();//观察目标更新状态}}interface Observer{//抽象观察者类void update(String id);}class passenger implements Observer{//具体观察者类private String name=null;public passenger(String name){this.name=name;}public void update(String id){System.out.println(name+" get in the car NO:"+id);}}class Observable{//观察目标类private List<Observer> observers=null;//观察者的属性集合protected String id=null;public Observable(String id){this.id=id;this.observers=new ArrayList<Observer>();}public void add(Observer o){this.observers.add(o);}public void delete(Observer o){this.observers.remove(o);}public void Notify(){//发出更新通知for(int i=0;i<observers.size();i++){observers.get(i).update(this.id);}}public void change(){}//待覆盖的更新状态事件}class Bus extends Observable{//具体目标类public Bus(String id){super(id);}public void change(){//更新事件System.out.println("列车进站。。。");Notify();}}
此处只是演示了目标对象的更新事件,对观察者的通知机制,并没有在目标对象中包含具体的状态,也没有进行对并发的处理。

更新函数参数

1、关于示例中的update函数的参数,在此例中观察目标并没有包含具体的状态,所以在通知观察者的函数中并没有提供自身引用作为参数,简单说就是,观察者对于观察目标的更新,只需要根据自身做出更新即可,不需要借助观察目标的状态。

2、在一些特殊的或者极为简单的场景中这是可以的,但是在简单如上例中所说,乘客等车这样单调的情形中,仍然需要传递一个车辆id作为参数(多个车辆中标志乘客等待的是某一个),所以通常的update函数是需要提供参数的(下面有Java API参考)。例如在Java的事件处理中,需要有事件源(观察目标)、事件处理程序(观察者),还要有事件对象本身(需要传递的对象),这里的事件处理程序需要针对事件对象做出操作,即update中需要提供事件对象作为参数。

3、更为一般的情况则是,事件处理程序既需要事件对象,也需要事件源的状态,然后才能做出处理,即需要同时提供观察目标的状态和发生的事件对象,即update中需要包含两个参数。

参考源代码

JDK中提供有对观察者模式的支持,在java.util包中提供有Observer接口和Observable类

观察者接口

public interface Observer {/*当观察目标状态更新时,观察者的更新函数包括两个参数,一个是观察目标,用以提供观察目标的状态另一个则是事件对象,如果只需要根据目标做出更新,则arg为null */    void update(Observable o, Object arg);}
观察目标类

public class Observable {    private boolean changed = false;//状态更新的标志    private Vector obs;//用以保存观察者的集合    /** Construct an Observable with zero Observers. */    public Observable() {        obs = new Vector();    }/*在添加观察者的函数中可以看出,即便vector作为所谓的线程安全集合自带的同步机制也只是实现addElement方法是同步方法,并不能保证addObserver的同步所以addObserver操作仍然需要synchronized修饰在去除重复观察者方面,利用vector的contains方法,而不是set集合,原因猜测1.对观察者模式的支持提出的时间比较早,跟vector一样古老,所以用的是vector,至于没有更改估计跟vector一直用Enumeration至死都没有改用Iterator一样,太古老,所以直接放弃不用了。没有考证,只是猜测2.线程安全集合,虽然此处没看出在同步方法之上再覆盖一层同步方法有何益处(除otifyObservers之外,所有方法都是synchronized修饰的同步方法,且notifyObservers函数中是复制出新集合,vector数组是私有属性,而且整个类中的所有方法都不存在逃逸情况) */public synchronized void addObserver(Observer o) {        if (o == null)            throw new NullPointerException();        if (!obs.contains(o)) {            obs.addElement(o);        }    }public synchronized void deleteObserver(Observer o) {        obs.removeElement(o);    }/*如果只需要传递观察目标接口,则第二个参数为null甚至专门提供了一个无参的notifyObservers方法来实现 */    public void notifyObservers() {        notifyObservers(null);    }public void notifyObservers(Object arg) {        /*         * a temporary array buffer, used as a snapshot of the state of         * current Observers.         */        Object[] arrLocal;/*这段代码的作用是在观察目标状态更新后,将原来的观察者集合复制出来为allLocal这样可以保证在通知所有观察者的过程中,原来的集合仍然可以同时添加观察者或者删除观察者,类似于一种读写分离或者写时复制的思想(此时是在通知观察者过程中,原有vector可以存在其他行为,即复制完成后。复制过程是全程加锁的,vector的添加、删除观察者操作也是加锁的,所以复制过程是同步的)并发中存在情况有1.在复制过程之后,通知操作完成之前,此时新添加的观察者不会收到更新通知2.在复制过程之后,通知操作完成之前,此时新删除的观察者也会收到更新通知源码说明如下:*/        synchronized (this) {            /* We don't want the Observer doing callbacks into             * arbitrary code while holding its own Monitor.             * The code where we extract each Observable from             * the Vector and store the state of the Observer             * needs synchronization, but notifying observers             * does not (should not).  The worst result of any             * potential race-condition here is that:             * 1) a newly-added Observer will miss a             *   notification in progress             * 2) a recently unregistered Observer will be             *   wrongly notified when it doesn't care             */            if (!changed)                return;            arrLocal = obs.toArray();            clearChanged();        }        for (int i = arrLocal.length-1; i>=0; i--)            ((Observer)arrLocal[i]).update(this, arg);    }public synchronized void deleteObservers() {        obs.removeAllElements();    }protected synchronized void setChanged() {        changed = true;    }protected synchronized void clearChanged() {//恢复更新状态        changed = false;    }public synchronized boolean hasChanged() {        return changed;    }public synchronized int countObservers() {        return obs.size();    }}

总结

观察者模式适用于对象的状态更新会产生对其他对象的影响,即需要其他对象针对该对象的更新操作作出自己的更新/调整操作。较好的类之间的耦合情况应该是,一个类实例的更新只对一个或很少的其他实例产生影响。

参考设计模式开闭原则,关于update函数的参数问题,如果无参数则具体目标类与具体观察者类无依赖关系,只是针对抽象类编程,满足开闭原则;如果需要以具体目标类作为参数,则如果需要的状态为抽象目标类的共有状态,则仍可以满足开闭原则,面向抽象编程,如果需要提供的目标类状态信息为具体目标类私有,则此时观察者类面向具体目标类编程,违反开闭原则,此种情形下抽象目标类的作用仅限于复用,不提供抽象编程的帮助。


0 0
原创粉丝点击