1.什么是观察者模式
2.要创建的三个接口
3.气象观察站例子的实现
4.用Java的内置的支持重做观察者模式
5.为什么把数据发给观察者之前要先调用setChanged()
(一)什么是观察者模式
观察者模式就如一个聊天室,当你需要收到聊天室的消息时,你就注册成为聊天室的成员,当聊天室有信息更新时,就会传到你那去。当你不需要接收聊天室的信息时,可以注销掉,退出聊天室。
又例如,天气观测站和气象报告板的关系。但报告板想获取观测站的数据,可以注册加入到观测站的观察者列表中,这就可以使观测站有数据更新时,自动传给气象报告板。
(二)要创建的三个接口
分别为:
1.Subject接口 :用于指定天气观测站(数据源)应该要具有的方法
addObserver() :用于加入一个气象报告板(观察站)到观察者列表中
deleteObserver() :用于从观察者列表中删除一个观察者
notifyObservers() :用于向观察者列表的所有观察者发送数据
setChanged() :当调用 notifyObserver() 之前,必须先调用一次setChanged()
2.Observer接口
update() :当从数据源处获得数据后,调用update()更新报告板(观察者)的数据。
3.DisplayElement接口
display() :用于显示报告板的数据
Subject 对应的是 数据源!!!
(三)气象观测站例子的实现
WeatherData 相当于气象观测站 和 观察者的 一个中介。WeatherData 获取来自气象观测站的数据后,向观察者发送数据。
目录树:
(1)Subject接口:
package com.test;
public interface Subject {
public void registerObserver(Observer o); //数据源提供用于 ( 观察者想要获取数据源时调用此方法注册)
public void removeObserver(Observer o); //数据源提供用于 (观察者不再需要数据源数据时,退出注册)
public void notifyObservers(); //数据源向注册在案的所有观察者发送数据
}
(2)Observer接口:
package com.test;
public interface Observer {
public void update(double temp,double humidity,double pressure);//观察者当接收到来自数据源的信息时,调用此方法更新自身数据
}
(3)DisplayElement接口:
package com.test;
public interface DisplayElement {
public void display(); //观察者调用此方法向用户显示数据
}
(4)WeatherData类:
package com.test;
import java.util.ArrayList;
public class WeatherData implements Subject{
private ArrayList observers;
private double temperature;
private double humidity;
private double pressure;
/*当数据源初始化时,维护一个记录观察者的列表*/
public WeatherData() {
observers = new ArrayList();
}
/*实现Subject接口的注册方法*/
public void registerObserver(Observer o) {
observers.add(o);
}
/*实现Subject接口的取消注册方法*/
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>=0) {
observers.remove(i);
}
}
/*实现Subject接口的notifyObservers(),用于向所有注册的观察者发送数据*/
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();
}
/*向获取数据的设备提供接口,当气象观察站获得新数据,会调用此接口向WeatherData写数据,以便WeatherData向观察者发送数据*/
public void setMeasurements(double temp,double humditidy,double pressure) {
this.temperature = temp;
this.humidity = humditidy;
this.pressure = pressure;
measurementsChanged(); //表示有了新数据
}
}
(5)ConditionDisplay类:(观察者)
package com.test;
public class ConditionDisplay implements Observer,DisplayElement{
private double temperature;
private double humidity;
private double pressure;
private String name;
private Subject weatherdata; // 观察者也要维护一个中介(WeatherData)作为数据源
/*当观察者初始化时,需要指定数据源和观察者的名字*/
public ConditionDisplay(Subject weatherdata,String name) {
this.name = name;
this.weatherdata = weatherdata;
weatherdata.registerObserver(this); //向数据源注册,代表需要从数据源获取数据
}
/*把数据打印给用户*/
public void display() {
System.out.println("观察者"+name+"的数据:");
System.out.println("temperature:"+temperature);
System.out.println("humidity:"+humidity);
System.out.println("pressure:"+pressure);
System.out.println();
}
/*当从数据源(WeatherData)获得数据后,用新数据更新自身数据*/
public void update(double temp, double humidity, double pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
display(); //信息更新完后,自动打印
}
}
(6)WeatherStation类:(主函数,气象观测站)
package com.test;
public class WeatherStation {
public static void main(String[] args) {
/*数据源和观察者都要维护一个 中介角色WeatherData*/
WeatherData weatherdata = new WeatherData();
/*以下为测试所用而创建3个观察者,并非数据源的作用*/
ConditionDisplay cd1 = new ConditionDisplay(weatherdata,"obser1");
ConditionDisplay cd2 = new ConditionDisplay(weatherdata,"obser2");
ConditionDisplay cd3 = new ConditionDisplay(weatherdata,"obser3");
/*数据源获得数据,并调用WeatherData的方法,来向WeatherData 写数据*/
weatherdata.setMeasurements(10.5, 10.4, 10.8);
}
}
运行结果:
观察者1的数据:
temperature:10.5
humidity:10.4
pressure:10.8
观察者2的数据:
temperature:10.5
humidity:10.4
pressure:10.8
观察者3的数据:
temperature:10.5
humidity:10.4
pressure:10.8
(四)用Java的内置的支持重做观察者模式
在 java.util 里为观察者模式提供了 一个Observable类(给数据源继承) 和 一个 Observer接口(给观察者实现)
import java.util.Observable;
import java.util.Observer;
Observable类中的方法,java已经帮我们实现好了。可以直接调用
目录树:
1.DisplayElement.java接口(与上同)
2.WeatherData.java类(数据源中介):
package com.test;
import java.util.Observable;
import java.util.Observer;
public class WeatherData extends Observable{
private double temperature;
private double humidity;
private double pressure;
public WeatherData() {}
/*向观察者发送数据*/
public void measurementsChanged() {
setChanged(); //向观察者发送数据之前必须先调用这个
notifyObservers(); //个人认为notifyOberservers()会自动调用观察者的 update()方法
}
/*从数据源收到数据后,是先更新自己的数据,再把数据发给观察者*/
public void setMeasurements(double temp,double humidity,double pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
/*留出接口给观察者获取数据*/
public double getTemperature() {
return temperature;
}
public double getHumidity() {
return humidity;
}
public double getPressure() {
return pressure;
}
}
package com.test;
import java.util.Observable;
import java.util.Observer;
public class ConditionDisplay implements Observer,DisplayElement{
private double temperature;
private double humidity;
private double pressure;
private Observable observable; // 任何数据源的引用
/*当构建观察者时,必须指定其 数据源*/
public ConditionDisplay(Observable observable,String name) {
this.observable = observable;
observable.addObserver(this); //注册
}
public void display() {
System.out.println("temperature:"+temperature+" humidity:"+humidity+" pressure:"+pressure);
}
public void update(Observable o, Object arg) {
if(o instanceof WeatherData) {
WeatherData w = (WeatherData)o;
this.temperature = w.getTemperature();
this.humidity = w.getHumidity();
this.pressure = w.getPressure();
display();
}
}
}
4.WeatherStation.java(Main函数)
package com.test;
import java.util.Observable;
import java.util.Observer;
public class WeatherStation {
public static void main(String[] args) {
/*数据源和观察者都要维护一个 中介角色WeatherData*/
WeatherData weatherdata = new WeatherData();
/*以下为测试所用而创建3个观察者,并非数据源的作用*/
ConditionDisplay cd1 = new ConditionDisplay(weatherdata,"观察站1");
/*数据源获得数据,并调用WeatherData的方法,来向WeatherData 写数据*/
weatherdata.setMeasurements(10.5, 10.4, 10.8);
}
}
运行结果:
temperature:10.5 humidity:10.4 pressure:10.8
(五)为什么把数据发给观察者之前要先调用setChanged()
因为数据有时更新得太快,如果想要数据在某一间隔才发给观察者,例如没5秒采集数据一次。逻辑上可以5秒后调用setChanged()一次,才能向观察者发送数据。
所以setChanged()用于控制数据的发送时机