转载请注明本文出自1124117571的博客(www.1124117571.iteye.com),谢谢支持!
观察者模式(Observer)行为型模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
OO原则:为了交互对象之间的松耦合设计而努力
当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。具体过程,假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。
下面拿气象站的例子来说明观察者模式
下面是气象站例子的UML图
![](https://i-blog.csdnimg.cn/blog_migrate/c140724b3f20db306fa0cfbfef22b27a.png)
WeatherStation的UML图
下面是UML图中各个成分的具体代码:
气象站的布告板:
/*
* 气象站的布告板
*/
public interface Subject {
public void registerObserver(Observer o); // 用来注册观察者
public void removeObserver(Observer o); // 用来移除观察者
public void notifyObservers(); // 当主题状态改变时,这个方法会被调用以通知所有的观察者
}
观察者接口:
/*
* 所有的气象组件都实现此观察者接口
* 所有的观察者都必须实现update方法,以实现观察者接口
*/
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
当布告板需要显示时,调用此方法
/*
* 当布告板需要显示时,调用此方法
*/
public interface DisplayElement {
public void display();
}
WeatherData对象(追踪来自气象站的数据,并更新布告板)
/*
* WeatherData对象(追踪来自气象站的数据,并更新布告板)
*/
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temperature; // 温度值
private float humidity; // 湿度值
private float pressure; // 气压值
public WeatherData(){
observers = new ArrayList<Observer>();
}
// =====================Subject接口的实现================================
public void registerObserver(Observer o){ // 当注册观察者时,我们只需要把它加到ArrayList的后面即可
observers.add(o);
}
public void removeObserver(Observer o){
int i = observers.indexOf(o);
if(i > 0){ // 说明ArrayList中存在该观察者
observers.remove(i);
}
}
public void notifyObservers(){
for(int i = 0;i < observers.size();i++){
Observer observer = (Observer)observers.get(i);
// 通过观察者的接口向观察者发送通知
observer.update(temperature, humidity, pressure);
}
}
// =================================================================
// 当从气象站得到更新观测值时,我们通知观察者
public void messurementsChanges(){
notifyObservers();
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
messurementsChanges();
}
// WeatherData的其他方法
}
建立布告板
/*
* 此布告板实现了Observer接口,所以可以从WeatherData对象中获得改变
* 规定了所有的布告板都必须实现DisplayElement接口
*/
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData; // 需要weatherData对象作为注册用
weatherData.registerObserver(this);
}
// 把最近的温度和湿度显示出来
@Override
public void display() {
System.out.println("Current conditions:" + temperature + "F degrees and " + humidity + "% humidity");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
}
测试程序WeatherStation
/**
* 测试程序WeatherStation
* @author asus
*
*/
public class WeatherStation {
public static void main(String[] args){
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);
// 模拟新的气象测量
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
测试结果
以上都是通过自己的努力从无到有地完成了观察者模式,但是Java API有内置的观察者模式。
java.util包内包含最基本的Observer接口和Observable类
Observable类追踪所有观察者并通知他们
Java内置的观察者模式如何运作:
如何把对象变成观察者?
答:实现观察者接口(java.util.Observer),然后调用任何Observer对象的addObserver方法,不想再当观察者时,调用deleteObserver()方法就可以了。
观察者如何送出通知?
答:1.首先需要李勇扩展java.util.Observer接口产生“可观察者”类。
2.先调用setChanged()方法,标记状态已经改变的事实。
3.然后调用两种notifyObservers()方法中的一个。
setChanged()方法的用处?
答:setChanged()方法用来标记状态已经改变的事实,好让notifyObservers()知道当它被调用时应该更新观察者,如果调用notifyObservers()之前没有先调用setChanged(),观察者就不会被通知。
Obserser类内部伪代码
setChanged(){ changed = true; // setChanged()方法把changed标志设为true } // notifyObservers只会在changed标为true时通知观察者 notifyObservers(Object args){ if(changed()){ for every Observer on the list{ can udpate(this,args); } changed = false; // 在通知观察者之后,把changed标志设置为false }
下面就进行改造,改造成使用Java内置的观察者模式
1.把WeatherData改成使用java.util.Observable
public class WeatherData extends Observable{
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){}
public void measurementsChanged(){
setChanged(); // 在调用notifyObservers()之前,要先调用setChanged()来指示状态已经改变
notifyObservers(); // 没有调用notifyObservers()传送数据对象,我们采用的做法是"拉"
}
//=======================================================
// 观察者会利用下面这些方法取得WeatherData对象的状态
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature(){
return temperature;
}
public float getHumidity(){
return humidity;
}
public float getPressure(){
return pressure;
}
//=======================================================
// WeatherData的其他方法
}
注意:
继承Observable表明该类是发送者(消息提供者)。
继承Observer表明该类是接收者(观察者)。
2.重做CurrentConditionDisplay
public class CurrentConditionDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature + "F degress and " + humidity + "% humidity");
}
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData) obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
}
测试结果:
Java内置的观察者模式的缺点:
1.Observable是一个类,从而限制了Observable的潜力,因为没有Observable接口,我们无法建立自己的实现和Java内置的Observer API配套使用。
2.违反了OO设计原则:多用组合,少用继承,因此除非你继承自Observable,否则将无法创建Observable实例并组合到我的对象中。