Java设计模式之观察者模式

来源:互联网 发布:xmind for mac 序列号 编辑:程序博客网 时间:2024/05/16 11:50

Java设计模式之观察者模式

简介

  在上一篇文章中介绍了设计模式的概念和最简单的设计模式-单例模式,单例模式之所以简单,是因为我们使用的时候只需要记住一点,那就是”独一无二的对象”就可以。今天我们要介绍的观察者模式相比单例模式肯定要复杂一些。但是只要你认真看完我写的这些东西,掌握观察者模式对你来说就不是什么难事,现在就让我们开始吧!

1.观察者模式

  首先你需要了解什么是观察者模式。在介绍观察者模式之前,我先给你举个例子,看了这个例子之后,你就明白了。比如现在一款知名的杂志,上面有很多精彩的内容,为了不错过每一期,你可能会去和杂志社的主编说”hey,主编,我要订阅这份杂志”。当然别的人也可以订阅这份杂志。所以结果就是,每当出了新的杂志,所有订阅了这份杂志的人都会第一时间收到这份杂志。如果有一天你不想继续订阅了,你去取消订阅,当以后再有更新也就不会再通知你了。这就是观察者模式的一种典型例子。
  观察者模式是一种基于订阅-通知类型的设计模式。也就是说如果我们对某件事情感兴趣(例如杂志),那么我们必须先去订阅它,然后当有更新时候会通知每一个订阅了该事件的对象。观察者模式定义了对象之间一种一对多的依赖关系,当一个对象的状态更新时,它的依赖者就会收到这种改变并自动更新。我们通常将被订阅的对象叫做主题对象,订阅的对象叫做订阅对象。可以用如下的图来帮助理解:

Created with Raphaël 2.1.0观察者模式示意图订阅者A订阅者A主题对象主题对象订阅者B订阅者B主题对象,我要订阅你我接受你的订阅主题对象,我要订阅你我接受你的订阅当我有更新时我要通知订阅者A我要通知订阅者B

  所以 观察者模式=订阅者+主题对象

2.代码实现

  为了帮助理解,我们来考虑这样一种场景。比如说市中心新建了一套房子,很多人都对这套房子感兴趣,但是觉得现在价格较贵,所以他们会说”当这套房子降价的时候务必第一时间告诉我”。通过这种场景,来说明具体怎么用代码实现观察者模式。
  通过上面这个例子,所以我们想到,可能需要如下的几个类。买家A,买家B,房子类。买家A和买家B都需要能够订阅房子,所以他们可能需要实现相同的接口。房子需要有通知订阅者的功能,所以也可能需要实现某个接口。所以在观察者模式中,订阅者需要实现同一个订阅接口(我们叫做Observer接口),被订阅者需要实现被订阅接口(我们叫做Observable接口)。既然清楚了需要哪些类,那么我们就开始代码来实现。

2.1订阅者接口

思考一下订阅者接口需要哪些功能。当我们订阅了某个事件,如果事件有更新,所有订阅者就会收到这个更新来更新自己的状态。Observer接口定义如下:
public interface Observer {    void update(Object object);}

其中update方法接受object参数,表示更新时接收的数据,比如房价。

2.2被订阅者接口

思考一下被订阅者需要哪些功能。首先需要能够添加订阅者,删除订阅者,其次还需要能够通知更新。所以将Observable接口定义如下:
public interface Observable {    void addObserver(Observer observer);    void removeObserver(Observer observer);    void notifyUpdate(Object object);}

2.3买家类

买家类需要实现Observer接口。实现类如下:
public class Customer implements Observer {    private String name;    public Customer(String name) {        this.name = name;    }    public String getName() {        return name;    }    @Override    public void update(Object object) {        if (object instanceof Float) {            System.out.println(getName() + " 收到了价格更新,更新后的价格是" + object);        }    }}

2.4房子类

房子类需要实现Observable接口。实现如下:
import java.util.ArrayList;import java.util.List;public class House implements Observable {    private double price;    private List<Observer> observers;    public House() {        observers = new ArrayList<>();        setPrice(1000000.00);//初始价格100万    }    public void setPrice(double price) {        System.out.println("House setPrice " + price);        this.price = price;        notifyUpdate(price);    }    public double getPrice() {        return price;    }    @Override    public void addObserver(Observer observer) {        observers.add(observer);    }    @Override    public void removeObserver(Observer observer) {        observers.remove(observer);    }    @Override    public void notifyUpdate(Object object) {        if (object instanceof Double) {            for (Observer observer : observers) {                observer.update(object);            }        }    }}

在House类中,我们使用ArrayList容器类存储所有的Observer对象。House有一个price属性,当调用setPrice方法的时候,我们就认为房子的价格发生了变化,此时就需要去通知所有的订阅者更新。

2.5测试类

测试类定义如下:
public class ObserverTest {    public static void main(String[] args) {        //新建两个买家对象        Customer customerA = new Customer("CustomerA");        Customer customerB = new Customer("CustomerB");        Customer customerC = new Customer("CustomerC");        //新建一个房子对象        House house = new House();        //买家订阅房子对象        house.addObserver(customerA);        house.addObserver(customerB);        house.addObserver(customerC);        //房子降价到90万        house.setPrice(900000);    }}

此时运行代码,得到如下的结果:

House setPrice 1000000.0House setPrice 900000.0CustomerA 收到了价格更新,更新后的价格是900000.0CustomerB 收到了价格更新,更新后的价格是900000.0CustomerC 收到了价格更新,更新后的价格是900000.0

所以可以看到A,B,C确实是收到了价格的更新。可能你会问,如果我有一天我不想买了,我不再需要更新信息了怎么办?很简单,你只需要取消订阅就可以。比如,买家B不再订阅。如下:

package test;public class ObserverTest {    public static void main(String[] args) {        //新建两个买家对象        Customer customerA = new Customer("CustomerA");        Customer customerB = new Customer("CustomerB");        Customer customerC = new Customer("CustomerC");        //新建一个房子对象        House house = new House();        //买家订阅房子对象        house.addObserver(customerA);        house.addObserver(customerB);        house.addObserver(customerC);        //房子降价到90万        house.setPrice(900000);        //买家B取消订阅        house.removeObserver(customerB);        //房子降价到80万        house.setPrice(800000);    }}运行代码得到如下的结果:House setPrice 1000000.0House setPrice 900000.0CustomerA 收到了价格更新,更新后的价格是900000.0CustomerB 收到了价格更新,更新后的价格是900000.0CustomerC 收到了价格更新,更新后的价格是900000.0House setPrice 800000.0CustomerA 收到了价格更新,更新后的价格是800000.0CustomerC 收到了价格更新,更新后的价格是800000.0

可以很清楚的看到,第一次价格更新,三个买家都收到了更新,但是B取消了订阅之后,B就再也不会受到更新的消息,如果有一天又想收到,那么重新订阅就可以,我们的House可是个好脾气!不管你曾经是不是曾经抛弃过它,只要你再次需要它它就会接受你。

3.Java内置对观察者模式的支持

  可能有些人会想,你这也太麻烦了,这接口那接口的太多了,太难以记住,不用怕,java本身内置了对观察者模式的支持。在java的util包下有两个接口Observer和Observable两个接口。从这两个的取名上来看,和我们自己写的差不多。那么来看一下源码吧。
Observer.java

package java.util;/** * A class can implement the <code>Observer</code> interface when it * wants to be informed of changes in observable objects. * * @author  Chris Warth * @see     java.util.Observable * @since   JDK1.0 */public interface Observer {    void update(Observable o, Object arg);}

可以看出和我们的定义几乎差不多。
再来看看Observable。
Observable.java

package java.util;public class Observable {    private boolean changed = false;    private Vector<Observer> obs;    public Observable() {        obs = new 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);    }    public void notifyObservers() {        notifyObservers(null);    }    public void notifyObservers(Object arg) {        Object[] arrLocal;        synchronized (this) {            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();    }}首先来看看与我们自己定义的Observable有什么不同。1. java内置的Observable是一个类,所以我们需要的是继承它。2. java内置的使用Vector来存储Observer。3. 增加了同步操作。其它的其实和我们自己定义的差不多,只要你看了前面自定义的实现的过程,看懂这个其实并不难。

  那么现在就用java内置观察者模式来实现前面的实例吧。

3.1买家类

import java.util.Observable;import java.util.Observer;public class Customer implements Observer {    private String name;    public Customer(String name) {        setName(name);    }    @Override    public void update(Observable o, Object arg) {        if (arg instanceof Double) {            System.out.println(getName() + " 收到了价格更新,更新后的价格是 " + arg);        }    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

3.2房子类

import java.util.Observable;public class House extends Observable {    private double price;    public House() {        setPrice(1000000);    }    public double getPrice() {        return price;    }    public void setPrice(double price) {        System.out.println("House setPrice " + price);        this.price = price;        setChanged();//设置更新状态        notifyObservers(price);    }}

3.3测试类

public class ObserverTest {    public static void main(String[] args) {        //定义三个买家        Customer customerA = new Customer("CustomerA");        Customer customerB = new Customer("CustomerB");        Customer customerC = new Customer("CustomerC");        //定义一个房子        House house = new House();        //添加订阅        house.addObserver(customerA);        house.addObserver(customerB);        house.addObserver(customerC);        //更新价格        house.setPrice(900000);        //B取消订阅        house.deleteObserver(customerB);        //更新价格        house.setPrice(800000);    }}运行结果如下:House setPrice 1000000.0House setPrice 900000.0CustomerC 收到了价格更新,更新后的价格是 900000.0CustomerB 收到了价格更新,更新后的价格是 900000.0CustomerA 收到了价格更新,更新后的价格是 900000.0House setPrice 800000.0CustomerC 收到了价格更新,更新后的价格是 800000.0CustomerA 收到了价格更新,更新后的价格是 800000.0可以看到的是效果和自定义的效果是一样的。但是在使用系统内置的观察者模式的时候,更新(调用notifyObservers之前一定要先调用setChanged方法设置状态为更新状态,否则无法实现更新).

4.到底该用哪种方式

  前面介绍了两种实现观察者模式的方式,一种是自起炉灶,一种是用java内置的观察者模式。到底哪种好。
  首先,java.util.Observable是一个类!是的,它是一个类,而且更麻烦的是它没有实现任何的接口,这就从某种程度上限制了它的使用。java.util.Observable的实现有一些问题,但是这并不意味这它没有提供相应的功能,只是说它没有想象中的那么有用。因为它是一个类。所以说如果你能够拓展Observable类,那么这个类就是有用的,相反,你可能需要像我们前面那样自己来实现一个观察者模式,反正这也不是一个困难的过程。反正你已经掌握了观察者模式,你就应该善用它们。
  第二从代码的角度上来说,自己实现一个观察者模式也并不难,而且通过接口的方式来设计更符合java面向抽象编程的思想。
  所以从如上的角度分析,如果你可以在代码中拓展java.util.Observalbe类,那么就可以使用java内置观察者;相反如果你无法拓展这个类,那么就应该自己来实现一个观察者模式。

5.总结

  哇,花了这么长的篇幅,终于讲清了观察者这个非常棒的设计模式,因为有了观察者,你就不会错过任何你感兴趣的事情(前提是你订阅了它)。有了观察者,你就会消息灵通。
  好了,到了这里,这篇文章就要结束了。下一篇文章,咱们要讲解的是Java设计模式之装饰器模式

原创粉丝点击