1. 什么是观察者模式?
观察者模式即是(主题+观察者),当主题对象状态发生变化时,能将自身变化情况通知观察者,以至于观察者能做出相应的变化或者处理的设计模式。
主题和观察者关系如下图所示 :
设计模式的使用场景?
项目中当对象间存在一对多关系时,则使用观察者模式(Observer Pattern),主要用于解决当一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
2、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
1 自定义观察者模式简单实现
package com.jun.interf;
/**
* 主题接口
* 所有主题实现类均要实现此接口
* @author Administrator
*
*/
public interface Subject {
/**
* 注册观察者
* @param observer
*/
public void registerObserver(Observer observer);
/**
* 移除观察者
* @param observer
*/
public void removeObserver(Observer observer);
/**
* 通知所有观察者
*/
public void notifyObservers();
}
package com.jun.interfaceImpl;
import java.util.ArrayList;
import com.jun.interf.Observer;
import com.jun.interf.Subject;
/**
* 主题实现类
* @author Administrator
*
*/
public class WeatherData implements Subject {
//观察者容器
private ArrayList<Observer> observers;
private float temp;
private float humidity;
private float pressure;
@Override
public void registerObserver(Observer observer) {
// TODO Auto-generated method stub
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
// TODO Auto-generated method stub
int indexOf = observers.indexOf(observer);
if(indexOf!=-1)
observers.remove(observer);
}
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(Observer observer:observers){
observer.update(temp, humidity, pressure);
}
}
/**
* 数据变化调用的方法
*/
public void msgChange(){
notifyObservers();
}
/**
* 执行业务逻辑代码
*/
public void runBusinesTask(){
//通过API获取温度信息
//从数据库中获取温度信息判断数据是否变化
//其他业务逻辑处理
//通知观察者
msgChange();
}
}
package com.jun.interf;
/**
* 观察者
* @author Administrator
* 所有观察者均需要实现此接口
*/
public interface Observer {
/**
* 更新方法
* @param temp
* @param humidity
* @param pressure
*/
public void update(float temp,float humidity,float pressure);
}
package com.jun.interf;
/**
* 显示模板
* @author Administrator
*
*/
public interface DisplayElement {
/**
* 显示布告
*/
public void display();
}
package com.jun.interfaceImpl;
import com.jun.interf.DisplayElement;
import com.jun.interf.Observer;
import com.jun.interf.Subject;
/**
1. 状况布告板实现
2. @author Administrator
3. */
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temp;
private float humidity;
private float pressure;
private Subject subject;
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("==========temp:"+this.temp+"==humidity:"+this.humidity+"====pressure:"+this.pressure);
}
/**
* 我们创建出来的观察者实现类
* 需要注册到主题才能收到主题通知
* @param subject
*/
public CurrentConditionsDisplay(Subject subject){
this.subject=subject;
//简单实例代码
subject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
// TODO Auto-generated method stub
this.pressure=pressure;
this.temp=temp;
this.humidity=humidity;
}
}
*
z
3 JDK自带观察者模式实现
在java.util包下有主题类Observable类和观察者接口Observer,源代码如下
package java.util;
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
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) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
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;
}
/**
* Indicates that this object has no longer changed, or that it has
* already notified all of its observers of its most recent change,
* so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
* This method is called automatically by the
* <code>notifyObservers</code> methods.
*
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
*/
protected synchronized void clearChanged() {
changed = false;
}
/**
* Tests if this object has changed.
*
* @return <code>true</code> if and only if the <code>setChanged</code>
* method has been called more recently than the
* <code>clearChanged</code> method on this object;
* <code>false</code> otherwise.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#setChanged()
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* Returns the number of observers of this <tt>Observable</tt> object.
*
* @return the number of observers of this object.
*/
public synchronized int countObservers() {
return obs.size();
}
}
如上代码所示即是JDk自带的观察者模式实现类,当我们需要使用此方式实现观察者模式时,我们的自定义主题类需要继承Observable类,观察者需实现Observer接口即可使用观察者设计模式到具体应用中。
其中jdk还对主题对象数据的传递提供了两种方式,推(push)和拉(pull),当使用推方式调用主题的notifyObservers(Object arg)方法通知观察者,数据通过参数传递。
当使用拉时候使用notifyObservers()无参方式通知观察者。观察者通过主题的getxxx方法获取想要的数据值。
4 推(push)和拉(pull)两种方式的比较
推(push)会使得目标观察者收到多余无用的数据,但是其方式将主题对象的内部数据保护起来。观察者不知道主题的数据信息,扩展新的变化属性不方便。
拉(pull)的方式通过主题提供的getXxx方式获取到观察者自己在意的数据,这样可以降低对传递数据的误会,但是通过getXxx方式暴露了主题的内部数据。因此在实际使用中可衡量利弊选择对应的方式。