一句话:解决一对多的依赖关系。
定义:
定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
需要解决的问题:
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
如何解决:
使用面向对象技术,可以将这种依赖关系弱化。
优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
1.一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2.一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3.需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
应用实例:
AWT中的事件处理DEM(委派事件模型Delegation Event Model)就是使用观察者模式实现的。junit3也使用观察者实现事件监听。
UML类图:
观察者模式包含如下角色:
Subject: 目标
ConcreteSubject: 具体目标
Observer: 观察者
ObserverImpl: 具体观察者
demo:
1.观察者接口
/**
* 观察者
*
*/
public interface Observer {
public void update(String msg);
}
2.观察者实现类1
package com.pattern.observer;
public class ObserverImpl1 implements Observer {
@Override
public void update(String msg) {
System.out.println("观察者1,收到信息:"+msg);
}
}
3.观察者实现类2
package com.pattern.observer;
public class ObserverImpl2 implements Observer {
@Override
public void update(String msg) {
System.out.println("观察者2,收到信息:"+msg);
}
}
4.被观察者抽象类
package com.pattern.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 被观察者
*
*/
public abstract class Subject {
private List<Observer> obs = new ArrayList<Observer>();
public void addObserver(Observer obs){
this.obs.add(obs);
}
public void delObserver(Observer obs){
this.obs.remove(obs);
}
protected void notifyObserver(String msg){
for(Observer o: obs){
o.update(msg);
}
}
public abstract void doSomething();
}
5.被观察者具体类
package com.pattern.observer;
public class ConcreteSubject extends Subject{
@Override
public void doSomething() {
System.out.println("被观察者事件反生");
this.notifyObserver("hello");
}
}
6.测试类
package com.pattern.observer;
public class Client {
public static void main(String[] args) {
Subject sub = new ConcreteSubject();
sub.addObserver(new ObserverImpl1()); // 添加观察者1
sub.addObserver(new ObserverImpl2()); // 添加观察者2
sub.doSomething();
}
}
JDK自带的观察者模式
在JDK源码下的java.util.Observable和java.util.Observer,分别代表观察者模式中的Subject和Observer。
Observable
在源码中,有两个notifyObservers方法,其中的主要差别就是一个是自带内容,另一个自带内容为null,其实也就是JDK的Observer接口默认是同时支持推模型和拉模型,如下:
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
...
...
}
在第二个notifyObservers方法中的代码实现,有一同步代码段:
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();
}
这是为了保证潜在的竞争关系,所以在实现了Observable类中,如果数据源发生了变化,要通知所有的Observer的时候,要先调用setChanged()方法。
Observer
Observer类是一个接口,里面就只有一个update的方法:
/**
* 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);
其实这个方法就是同时支持拉模型(把Observable整个对象传递过来了,JDK默认就是传递了整个对象),拉模型(Object对象,对应着Observable里面特有的内容属性)。
demo(气象站):
1.被观察者
package com.pattern.observer;
import java.util.Observable;
/**
* 被观察者
*
*/
public class WeatherData extends Observable {
private float temperatrue;
private float humidity;
private float pressure;
public WeatherData(){};
public void measurementsChanged(){
setChanged();
notifyObservers();
}
public float getTemperature(){
return temperatrue;
}
public float getHumidity(){
return humidity;
}
public float getPressure(){
return pressure;
}
// 天气发生变化
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperatrue = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
2.观察者
package com.pattern.observer;
import java.util.Observable;
import java.util.Observer;
/**
*观察者
*
*/
public class CurrentConditionsDisplay implements Observer {
private Observable observable;
private float temperature;
private float pressure;
private float humidity;
public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
this.observable.addObserver(this);
}
@Override
public void update(Observable observable, Object obj) {
if(observable instanceof WeatherData){
WeatherData weatherData = (WeatherData)observable;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
this.pressure = weatherData.getPressure();
display();
}
}
public void display() {
System.out.println("当前温度:" + this.temperature);
System.out.println("当期湿度:" + this.humidity);
System.out.println("当期气压:" + this.pressure);
}
}
3.测试
package com.pattern.observer;
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay CurrentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(23, 35, 12);
}
}