文章思路都来自于HeadFirst
观察者模式简介
定义:观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己
设计场景
设计一个系统 :显示目前的状况的布告板,当weatherObject 获得最新的测量数据的时候将实时更新到布告板上。此系统的三个部分气象站,weatherdata对象(追踪来自气象站的数据,并更新布告板,可能有多个布告板分别显示不同的数据),布告板显示数据。
第一个实现
public class WeatherData {
public void measurementsChanged() {
private CurrentConditionsDisplay currentConditionsDisplay;
private ForecastDisplay forecastDisplay;
float temperature = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
List<Float> forecastTemperatures = getForecastTemperatures();
currentConditionsDisplay.update(temperature, humidity, pressure);
forecastDisplay.update(forecastTemperatures);
}
}
实现的思路非常简单,我们的“受观察对象”从气象台获取测量数据,并保存布告板具体实现的引用,当测量数据改变的时候,调用每个布告板的update方法,实时更新数据。我们来分析这段代码的问题,首先我们使用具体实现的引用来调用更新方法,违反了设计原则:针对接口编程而非具体实现编程。当每次要添加新的布告板的时候,我们的WeatherData都得添加新的引用,这是可变的部分,我们没有封装容易改变的部分,而且我们无法在运行时动态的改变布告板。每个布告板都是我们手动的添加或者删除引用。 接下来我们采用观察者模式来解决这个问题
观察者模式实现
从此图中看出,主题(被观察者)并不需要知道观察者的具体类是谁,做了些什么或者其他任何细节。任何时候都可以增加新的观察者,他唯一依赖的就是一个实现了Observer接口的对象列表。所以我们要遵守一个设计原则:为了交互对象之间的松耦合设计而努力。松耦合的设计把对象之间的互相依赖降到了最低。
下面是实现的代码:
package designPattern;
import java.util.ArrayList;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
//主题状态改变,此方法会被调用通知所有的观察者
public void notifyObservers();
}
interface Observer{
//当气象观测值改变时候,主题会把这些状态值当做方法的参数,传送给观察者
void update (float temp,float humidity,float pressure);
}
interface DispalyElement{
// 当布告板需要显示的时候,调用此方法
void display();
}
class WeatherData implements Subject{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers=new ArrayList();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i=observers.indexOf(o);
if(i>0){
observers.remove(o);
}
}
//这边注意只要是有变化,就会通知所有的观察者,类似于全体推送消息
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
Observer observer= (Observer) observers.get(i);
observer.update(temperature,humidity,pressure);
}
}
//一旦观测值变化就需要通知观察者
public void measurementsChanged(){
notifyObservers();
}
//利用这个方法来改变观测值
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
//目前状况布告板
class CurrentConditionsDisplay implements Observer,DispalyElement{
private float temperature;
private float humidity;
private Subject weatherData;
CurrentConditionsDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature=temperature;
this.humidity=humidity;
display();
}
@Override
public void display() {
System.out.println("Current conditions:"+temperature+"F degree and"+humidity+"% humidity");
}
}
class WeatherStation{
public static void main(String[] args) {
WeatherData weatherData=new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay=new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80,65,30.3f);
weatherData.setMeasurements(78,66,30.4f);
}
}
这段代码挺容易理解,但是有个疑问,为什么总是受观察者的数据改变之后就推送给观察者,为什么不能是观察者自主的选择要看的数据呢?
所以还有一种“拉数据”的观察者模式,java内置的观察者模式,“推拉”形式的都有,我们来看下java内置的怎么实现
package designPattern;
import java.util.Observable;
import java.util.Observer;
interface DispalyElement{
// 当布告板需要显示的时候,调用此方法
void display();
}
//注意这里是继承
class WeatherData extends Observable {
/*我们不在需要追踪观察者了,也不需要管理注册与删除(让超类去做)
所以删除了相关的代码
所以构造器也不需要为了记住观察者而建立数据结构
* */
private float temperature;
private float humidity;
private float pressure;
WeatherData(){ }
void measurementsChanged(){
//调用此方法表示状态已经改变。
setChanged();
//这是父类的方法,没有传送数据对象,表示我们的采用的做法是“拉”
notifyObservers();
}
//利用这个方法来改变观测值
void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
//观察者利用这些方法取得WeatherData对象的状态。
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public float getTemperature() {
return temperature;
}
}
//目前状况布告板
class CurrentConditionsDisplay implements Observer,DispalyElement{
private float temperature;
private float humidity;
//主题对象父类,面向接口编程
Observable observable;
CurrentConditionsDisplay(Observable observable){
this.observable=observable;
//登记成为观察者
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions:"+temperature+"F degree and"+humidity+"% humidity");
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData=(WeatherData)o;
this.temperature=weatherData.getTemperature();
this.humidity=weatherData.getHumidity();
display();
}
}
}
class WeatherStation{
public static void main(String[] args) {
WeatherData weatherData=new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay=new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80,65,30.3f);
weatherData.setMeasurements(78,66,30.4f);
}
}
理解上面代码最好对应JDK的源码,这里我附上源码供参考
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;
}
java.util.Observable#notifyObservers(java.lang.Object)
*/
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
但是要注意java内置的观察者模式有太多局限,比如它是一个类,我们必须要继承,限制了Observable的复用潜力。
总结一下:
在学习观察者模式的过程中,我们明白了一条设计规则:尽量降低交互对象之间的耦合度
1)受观察者不知道观察者的细节,只知道观察者实现了观察者接口(Update方法)
2)使用此模式可从 被观察者处推或者拉数据
3)如果有必要,最好自己实现Observable
4)在观察者模式中,会改变的是受观察者的状态(数据),以及观察者的数目和类型。用这个模式可以改变依赖于受观察者的对象,而不必更改受观察者(找出程序中会变化的部分,然后将其和固定不变得分离)
5)受观察者和观察者都使用接口:观察者利用受观察者的接口向受观察者注册,而受观察者利用观察者接口通知观察者,这样可以让两者之间正确运作,并具有松耦合的特点(针对接口编程,而不针对实现编程)
6)观察者模式利用“组合”将许多观察者自合进受观察者中。对象之间的这种关系不是通过继承产生的,而是在运行期间通过组合的方式产生的。
使用场景
使用观察者模式的场景和优缺点
使用场景
关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优点
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
参考博客:http://blog.csdn.net/itachi85/article/details/50773358
课后小练习:
package designPatternExe.observer;
import java.util.ArrayList;
/*
* * 场景描述:
* 哈票以购票为核心业务(此模式不限于该业务),但围绕购票会产生不同的其他逻辑,如:
* 1、购票后记录文本日志
* 2、购票后记录数据库日志
* 3、购票后发送短信
* 4、其他各类活动等(考虑程序以后的扩展性)
**/
/*分析:找到受观察者和观察者
受观察者:购票行为
观察者:看到购物后的一系列动作
核心:观察者和被观察者各自有自己的接口
* */
public class obs_1 {
public static void main(String[] args) {
//1:创建主题(被观察者)并且注册了通知(构造函数中)
Observable observable=new SoldOut();
//2:创建观察者
Observer observer=new LogTextLog(observable);
Observer observer1=new LogDatabaseLog(observable);
Observer observer2=new SendMessage(observable);
//受观察者通知观察者购票行为改变
observable.setChanged();
}
}
//受观察者
interface Observable{
//添加观察者
void registerObserver(Observer o);
//删除观察者
void deleteObserver(Observer o);
//通知观察者
void notifyObservers();
void setChanged();
}
//售票行为需要被观察
class SoldOut implements Observable{
//观察者引用
private ArrayList<Observer> observers;
public SoldOut(){
observers=new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void deleteObserver(Observer o) {
int i=observers.indexOf(o);
if(i>0){
observers.remove(i);
}
}
@Override
public void notifyObservers() {
//通知所有观察者
System.out.println("购票行为产生了");
for (int i = 0; i < observers.size(); i++) {
Observer o=observers.get(i);
o.update();
}
}
@Override
public void setChanged() {
notifyObservers();
}
}
//定义观察者
interface Observer{
//更新 被通知后做的事,不同的行为有不同的实现
void update();
}
class LogTextLog implements Observer{
private Observable observable;
public LogTextLog(Observable observable){
this.observable=observable;
observable.registerObserver(this);
}
@Override
public void update() {
System.out.println("购票后记录文本日志");
}
}
class LogDatabaseLog implements Observer{
private Observable observable;
public LogDatabaseLog(Observable observable){
this.observable=observable;
observable.registerObserver(this);
}
@Override
public void update() {
System.out.println("购票后记录数据库日志");
}
}
class SendMessage implements Observer{
private Observable observable;
public SendMessage(Observable observable){
this.observable=observable;
observable.registerObserver(this);
}
@Override
public void update() {
System.out.println("购票后发送短信");
}
}