Java设计模式之观察者模式
来源:互联网 发布:xmind for mac 序列号 编辑:程序博客网 时间:2024/05/16 11:50
Java设计模式之观察者模式
简介
在上一篇文章中介绍了设计模式的概念和最简单的设计模式-单例模式,单例模式之所以简单,是因为我们使用的时候只需要记住一点,那就是”独一无二的对象”就可以。今天我们要介绍的观察者模式相比单例模式肯定要复杂一些。但是只要你认真看完我写的这些东西,掌握观察者模式对你来说就不是什么难事,现在就让我们开始吧!
1.观察者模式
首先你需要了解什么是观察者模式。在介绍观察者模式之前,我先给你举个例子,看了这个例子之后,你就明白了。比如现在一款知名的杂志,上面有很多精彩的内容,为了不错过每一期,你可能会去和杂志社的主编说”hey,主编,我要订阅这份杂志”。当然别的人也可以订阅这份杂志。所以结果就是,每当出了新的杂志,所有订阅了这份杂志的人都会第一时间收到这份杂志。如果有一天你不想继续订阅了,你去取消订阅,当以后再有更新也就不会再通知你了。这就是观察者模式的一种典型例子。
观察者模式是一种基于订阅-通知类型的设计模式。也就是说如果我们对某件事情感兴趣(例如杂志),那么我们必须先去订阅它,然后当有更新时候会通知每一个订阅了该事件的对象。观察者模式定义了对象之间一种一对多的依赖关系,当一个对象的状态更新时,它的依赖者就会收到这种改变并自动更新。我们通常将被订阅的对象叫做主题对象,订阅的对象叫做订阅对象。可以用如下的图来帮助理解:
所以 观察者模式=订阅者+主题对象。
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设计模式之装饰器模式。
- java设计模式之“观察者设计模式”
- Java设计模式之观察者设计模式
- 《java设计模式》之观察者设计模式
- Java设计模式之观察者
- Java设计模式之观察者
- Java设计模式之Observer(观察者)模式
- Java设计模式之Observer 观察者模式
- Java设计模式之Observer-观察者模式
- java 设计模式之二-观察者模式
- Java设计模式之观察者模式
- java设计模式之观察者模式
- java设计模式之观察者模式Observer
- java设计模式之观察者模式
- Java设计模式之观察者模式
- JAVA设计模式之观察者模式2
- java与设计模式之观察者模式
- Java--设计模式之观察者模式
- java设计模式之观察者模式
- 【ZJOJ5186】【NOIP2017提高组模拟6.30】tty's home
- Hessian的使用以及理解
- DPDK有关的网址
- Metropolis-Hasting 算法 & 图上的Metropolis-Hasting Random Walk (MHRW)
- openSession()到底做了什么
- Java设计模式之观察者模式
- 学习unity能够做什么
- 5 函数的定义和调用应用实例
- 二十、用上下文管理应用
- Python 字符串常用函数
- MonkeyRunner:Android自动化测试
- Salesforce 知识整理
- mysqldump大致原理以及mysqldump备份过程中进行DDL操作的影响
- LINUX守护进程