缘由
之前看懂了的设计模式,不总结总结,总觉得心里很难受。我的理解
来个生活中的例子,有一天,一个朋友要坐公交来你家里玩,怕他找不到路,你们约定你到你家附近的公交站牌等他。假设你朋友坐公交都要一个多小时,那你什么时候去公交站牌等他呢?总部能一开始就去吧?那会白等一个小时的。你可以一直打电话问你朋友,过一会就打个电话问问他到哪里了,他告诉你之后你再决定是在家里等,还是去公交站牌等。过了一会你又打电话问他到哪里了。这样,一个小时内你打了几个电话来确定他的行踪。好笨哦,是吧,我们让朋友通知我们不就好了吗?我们老去问个屁啊。朋友到了要下的站牌的前一站,给我们打个电话,我们就知道要出发了撒。对了,这就是观察者模式。
我们就是观察者,朋友就是被观察者。我们可以一直打电话问朋友,这就是观察者一直去询问被观察者,也可以被观察者到了合适的时候主动通知观察者。显然后一种说法更为合理。
关键是还有一种情况,我们不得不考虑,那就是当有很多朋友都要来看你时,难道你要每一个朋友都过一会打一个电话,然后确定去不去公交站牌接人吗?显然不是,我们只需要要求所有朋友到了合适的时候给我们打电话。这就是一个观察者可以同时观察几个被观察者,而不用总是去询问,只需要被观察者自己的状态发生转变之后,那么通知观察者即可。
其实一个被观察者也可以拥有很多观察者,比如你的朋友还想告诉他的父母,他已经安全到了你的家了,他的父母就是另一个观察者。
当然书上举的例子也很不错,就像订报纸一样,订报人告就是观察者,报社是被观察者。订报人告诉报社,我要订报。那么报纸一生产出来就会被送给订报人。这就是观察者的状态发生了改变,报社生产了报纸,报社就会通知订报人,就是把报纸送给订报人。
ok,这个思路说清楚之后来看一下观察者的定义:
- 观察者模式定义了一系列对象之间的一对多关系。当一个对改变状态,其他依赖者都会收到。
类图
例子代码
有代码例子是气象站。天气的温度、湿度在不断的变化。气象局作为被观察者,电视上的天气预报的滚动条可以作为一个观察者(ForecastDisplay),气象局门口那个电子大展牌可以作为另一个观察者(CurrentConditionsDisplay),气象局的天气统计部门又是另一个观察者,等等等。一旦气象局知道了天气的变化,那么就会通知这些观察者。书中给出的类图如下:
- 其中DispalyElement接口其实和观察者模式没什么关系,只是用来作为显示的功能的。
实现代码
结构,其他的都是接口:/**
* 这是观察者模式中的主题接口,当主题发生变化是会通知观察者
* 所以可以添加观察者和移除观察者,通知观察者
* @author chouyou
*
*/
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
/**
* 这是观察者模式中观察者接口,
* 但是这是针对气象台的布告牌所写的观察者,所以在其update方法中,主要是针对气象数据作一些处理
* @author chouyou
*
*/
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
/**
* 这个恐怕和观察者模式没有什么关系,主要是和所举例子有关
* 该接口可以当作是一个显示器,而所有的布告牌都会安装一个显示器,用于显示相关数据
* @author chouyou
*
*/
public interface DisplayElement {
public void display();
}
import java.util.ArrayList;
import javax.xml.bind.NotIdentifiableEvent;
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temp;
private float humidity;
private float pressure;
public WeatherData() {
// TODO Auto-generated constructor stub
observers= new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
int i = observers.indexOf(o);
if (i >= 0 ) {
observers.remove(i);
}
}
@Override
public void notifyObserver() {
// TODO Auto-generated method stub
for(int i = 0; i < observers.size(); i ++){
Observer observer = (Observer) observers.get(i);
observer.update(temp, humidity, pressure);
}
}
/**
* 当气象台的数据发生变化时,就调用通知观察者的方法
*/
public void measurementsChanged(){
notifyObserver();
}
public void setMeasurements(float temp, float humidity, float pressure){
this.temp = temp ;
this.humidity = humidity ;
this.pressure = pressure ;
measurementsChanged();
}
}
/**
* 这是一个观察者的实现类,注意他必须持有他所感兴趣的主题的对象的引用
* @author chouyou
*
*/
public class CurrentConditionsDisplay implements Observer, DisplayElement {
/**
* 除了要显示的数据,必须要拥有一个主题的对象,这样才将自己这个观察者注册到主题里面去
* 这也暗示了这个观察者,对哪个主题感兴趣。
*/
private Subject weatherDate;
private float temp;
private float humidity;
public CurrentConditionsDisplay(Subject weatherDate) {
// TODO Auto-generated constructor stub
this.weatherDate = weatherDate;
weatherDate.registerObserver(this);
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("Current conditions: "+temp + "humidity:" + humidity);
}
@Override
public void update(float temp, float humidity, float pressure) {
// TODO Auto-generated method stub
this.temp = temp;
this.humidity = humidity;
display();
}
}
/**
* 这是我写的另一个观察者,比较简单。
* 只是为了证明,一个主题能够拥有多个观察者
* @author chouyou
*
*/
public class ForecastDisplay implements Observer,DisplayElement{
private float temp;
private WeatherData weatherData;
public ForecastDisplay(WeatherData weatherData) {
// TODO Auto-generated constructor stub
this.weatherData = weatherData ;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
// TODO Auto-generated method stub
this.temp = temp;
display();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("天气预报,今天的天气是:"+temp);
}
}
import test.CurrentConditionsDisplay2;
import test.WeatherData2;
/**
* 测试用的主类
* @author chouyou
*
*/
public class testMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
WeatherData weatherData = new WeatherData();
//实例化观察者们
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
//下面我们改变了天气的数据,那么上面注册的观察们由于用于注册了监听,所以发出回应。
weatherData.setMeasurements(80, 64, 30.4f);
weatherData.setMeasurements(70, 63, 20.4f);
weatherData.setMeasurements(76, 34, 32.4f);
}
}
运行结果:
Current conditions: 80.0humidity:64.0
天气预报,今天的天气是:80.0
Current conditions: 70.0humidity:63.0
天气预报,今天的天气是:70.0
Current conditions: 76.0humidity:34.0
天气预报,今天的天气是:76.0
利用java内置的观察者模式的接口
在java.util包中- 有一个类:Observable,就是被观察者。
- Observer接口就是观察者。
但是作为使用,我还是写了点代码来玩玩:
import java.util.Observable;
import java.util.Observer;//不要引错包了
public class CurrentConditionsDisplay2 implements Observer, DisplayElement {
Observable observable;//同样,我们需要一个可被观察者的对象
private float temp;
private float humidity;
public CurrentConditionsDisplay2(Observable observable ) {
// TODO Auto-generated constructor stub
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("内置观察者:Current conditions: "+temp + " humidity:" + humidity);
}
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
if(o instanceof WeatherData2){
WeatherData2 weatherData2 = (WeatherData2) o;
this.temp = weatherData2.getTemp();
this.humidity = weatherData2.getHumidity();
display();
}
}
}
import java.util.Observable;
public class WeatherData2 extends Observable {
private float temp;
private float humidity;
private float pressure;
//get方法是观察者需要调用的这个方法来获得WeatherData2的对象
public float getTemp() {
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
/**
* 当气象台的数据发生变化时,就调用通知观察者的方法
*/
public void measurementsChanged(){
setChanged();//表示状态已经改变了
notifyObservers();//通知所有的观察者
}
public void setMeasurements(float temp, float humidity, float pressure){
this.temp = temp ;
this.humidity = humidity ;
this.pressure = pressure ;
measurementsChanged();
}
}
/**
* 测试用的主类
* @author chouyou
*
*/
public class testMain {
public static void main(String[] args) {
WeatherData2 weatherData2 = new WeatherData2();
CurrentConditionsDisplay2 currentDisplay2 = new CurrentConditionsDisplay2(weatherData2);
weatherData2.setMeasurements(80, 64, 30.4f);
weatherData2.setMeasurements(70, 63, 20.4f);
weatherData2.setMeasurements(76, 34, 32.4f);
}
}
运行结果:
内置观察者:Current conditions: 80.0 humidity:64.0
内置观察者:Current conditions: 70.0 humidity:63.0
内置观察者:Current conditions: 76.0 humidity:34.0
java中使用到到观察者模式的地方
一个button可以成为一个被观察者,我们只需要很简单的java内的代码就可以完成一个观察者对这个button的注册。当这个button被点击时,就是状态发生改变后,他就会通知他的观察者们。源代码
- 上传网盘:观察者模式