android学习日记-6 观察者模式与订阅者模式

来源:互联网 发布:中铁七局郑州公司知乎 编辑:程序博客网 时间:2024/06/11 20:03

      观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

     观察者模式属于行为模式,其意图是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在制作系统的过程中,将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了他们的可重用性。这一个模式的关键对象是目标和观察者。一个目标可以有任意数目的依赖它的观察者,一旦目标的状态发生改变,所有的观察者都得到通知,作为对这个通知的响应,每个观察者都将查询目标以使其状态与目标的状态同步。这种交互也称为发布-订阅模式,目标是通知的发布者。他发出通知时并不需要知道谁是他的观察者,可以有任意数据的观察者订阅并接收通知。

     适用性:在以下的任一情况下可以使用观察者模式:

     1  当一个抽象模型的两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中可以使他们各自独立的改变和复用。

      2 当对一个对象的改变需要同时改变其它对象,而不知道具体由多少对象有待改变。

      3 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,换言之,你不希望这些对象是紧密耦合的。


 观察者模式所涉及的角色有:

  ●  抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

  ●  具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

  ●  抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  ●  具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。


推模型和拉模型

  在观察者模式中,又分为推模型和拉模型两种方式。

  ●  推模型

     主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。

  ●  拉模型

     主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

  根据上面的描述,发现前面的例子就是典型的推模型,下面给出一个拉模型的实例。

  拉模型的抽象观察者类

  拉模型通常都是把主题对象当做参数传递。

 推模型例子:

抽象主题角色类:

public abstract class Subject {    /**     * 用来保存注册的观察者对象     */    private    List<Observer> list = new ArrayList<Observer>();    /**     * 注册观察者对象     * @param observer    观察者对象     */    public void attach(Observer observer){                list.add(observer);        System.out.println("Attached an observer");    }    /**     * 删除观察者对象     * @param observer    观察者对象     */    public void detach(Observer observer){                list.remove(observer);    }    /**     * 通知所有注册的观察者对象     */    public void nodifyObservers(String newState){                for(Observer observer : list){            observer.update(newState);        }    }}

  具体主题角色类

public class ConcreteSubject extends Subject{        private String state;        public String getState() {        return state;    }    public void change(String newState){        state = newState;        System.out.println("主题状态为:" + state);        //状态发生改变,通知各个观察者        this.nodifyObservers(state);    }}
  抽象观察者角色类

public interface Observer {    /**     * 更新接口     * @param state    更新的状态     */    public void update(String state);}

具体观察者角色类

public class ConcreteObserver implements Observer {    //观察者的状态    private String observerState;        @Override    public void update(String state) {        /**         * 更新观察者的状态,使其与目标的状态保持一致         */        observerState = state;        System.out.println("状态为:"+observerState);    }}
客户端类

public class Client {    public static void main(String[] args) {        //创建主题对象        ConcreteSubject subject = new ConcreteSubject();        //创建观察者对象        Observer observer = new ConcreteObserver();        //将观察者对象登记到主题对象上        subject.attach(observer);        //改变主题对象的状态        subject.change("new state");    }}

  运行结果如下

  在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察者对象。然后,它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的聚集中去。

  这时,客户端调用主题的change()方法,改变了主题对象的内部状态。主题对象在状态发生变化时,调用超类的notifyObservers()方法,通知所有登记过的观察者对象。

拉模型的抽象观察者类

  拉模型通常都是把主题对象当做参数传递。

public interface Observer {    /**     * 更新接口     * @param subject 传入主题对象,方面获取相应的主题对象的状态     */    public void update(Subject subject);}

 拉模型的具体观察者类

public class ConcreteObserver implements Observer {    //观察者的状态    private String observerState;        @Override    public void update(Subject subject) {        /**         * 更新观察者的状态,使其与目标的状态保持一致         */        observerState = ((ConcreteSubject)subject).getState();        System.out.println("观察者状态为:"+observerState);    }}

拉模型的抽象主题类

  拉模型的抽象主题类主要的改变是nodifyObservers()方法。在循环通知观察者的时候,也就是循环调用观察者的update()方法的时候,传入的参数不同了。

public abstract class Subject {    /**     * 用来保存注册的观察者对象     */    private    List<Observer> list = new ArrayList<Observer>();    /**     * 注册观察者对象     * @param observer    观察者对象     */    public void attach(Observer observer){                list.add(observer);        System.out.println("Attached an observer");    }    /**     * 删除观察者对象     * @param observer    观察者对象     */    public void detach(Observer observer){                list.remove(observer);    }    /**     * 通知所有注册的观察者对象     */    public void nodifyObservers(){                for(Observer observer : list){            observer.update(this);        }    }}

 拉模型的具体主题类

  跟推模型相比,有一点变化,就是调用通知观察者的方法的时候,不需要传入参数了。

public class ConcreteSubject extends Subject{        private String state;        public String getState() {        return state;    }    public void change(String newState){        state = newState;        System.out.println("主题状态为:" + state);        //状态发生改变,通知各个观察者        this.nodifyObservers();    }}

两种模式的比较

  ■  推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

  ■  推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。



参考文献:http://blog.csdn.net/zhangming1013/article/details/25248269

http://tianli.blog.51cto.com/190322/40455

源码:http://zwx622.iteye.com/admin/blogs/2068733

0 0