本文示例代码材料源自Head First设计模式
以前整理自己整理的观察者模式的链接:
https://blog.csdn.net/u011109881/article/details/59773041
思想
观察者模式是使用的比较普遍的设计模式,其核心思想是在被观察者(Observable/Subject)中放入观察者(Observer)的实例列表,一旦被观察者有数据更新,则遍历观察者列表,调用观察者方法来更新数据。
下面举个例子:比如一家气象站有气象数据,拥有历史 当前和未来数天的天气数据,现在要将这些数据显示到天气布告板上去,但是,布告板有详细的布告板也有简单的布告板,总之,数据可能给布告板的都是相同,但是布告板可能显示成不同的样子,或者布告板只取它需要的数据显示。这个例子可以用观察者模式来实现。
示例思路(规划类图)
UML结构图
实际代码
Bean
public class WeatherBean {
double temperature;
double humidity;
double pressure;
public double getTemperature() {
return temperature;
}
public void setTemperature(double temperature) {
this.temperature = temperature;
}
public double getHumidity() {
return humidity;
}
public void setHumidity(double humidity) {
this.humidity = humidity;
}
public double getPressure() {
return pressure;
}
public void setPressure(double pressure) {
this.pressure = pressure;
}
}
观察者的接口
public interface DisplayElement {
public void display(WeatherBean data);
}
观察者和其实现类
public interface Observer {
public void update(WeatherBean data);
}
public class OneObserverDisplay implements Observer, DisplayElement {
public WeatherStation mStation = null;
public OneObserverDisplay(WeatherStation station){
this.mStation = station;
mStation.registerObserver(this);
}
public void display(WeatherBean data) {
System.out.println("this is " + this.getClass().getName()
+ " getHumidity:" + data.getHumidity() + " getPressure:"
+ data.getPressure() + " getTemperature:"
+ data.getTemperature());
}
public void removeListener(){
this.mStation.removeObserver(this);
}
public void update(WeatherBean data) {
display(data);
}
}
public class TwoObserverDisplay implements Observer, DisplayElement {
public WeatherStation mStation = null;
public TwoObserverDisplay(WeatherStation station){
this.mStation = station;
mStation.registerObserver(this);
}
public void display(WeatherBean data) {
System.out.println("this is " + this.getClass().getName()
+ " getHumidity:" + data.getHumidity() + " getPressure:"
+ data.getPressure() + " getTemperature:"
+ data.getTemperature());
}
public void removeListener(){
this.mStation.removeObserver(this);
}
public void update(WeatherBean data) {
display(data);
}
}
ThreeObserverDisplay就不贴了,基本一样
被观察者和其实现类
public interface Subject {
void registerObserver(Observer ob);
void removeObserver(Observer ob);
void notifyObservers();
}
public class WeatherStation implements Subject{
private WeatherBean mWeatherBean;
//API 1.5
private ArrayList<Observer> mObservers = new ArrayList<Observer>();
public void setMeasurement(WeatherBean data){
mWeatherBean = data;
measurementChanged();
}
private void measurementChanged(){
notifyObservers();
}
public void registerObserver(Observer ob) {
if(!mObservers.contains(ob)){
mObservers.add(ob);
}
}
public void removeObserver(Observer ob) {
if(mObservers.contains(ob)){
mObservers.remove(ob);
}
}
public void notifyObservers() {
for(Observer observer : mObservers){
observer.update(mWeatherBean);
}
}
}
测试类
public class TestObserver {
public static void main(String[] args) {
WeatherStation station = new WeatherStation();
OneObserverDisplay one = new OneObserverDisplay(station);
TwoObserverDisplay two = new TwoObserverDisplay(station);
ThreeObserverDisplay three = new ThreeObserverDisplay(station);
WeatherBean data = new WeatherBean();
data.setHumidity(1.1);
data.setPressure(1.2);
data.setTemperature(1.3);
station.setMeasurement(data);
one.removeListener();
station.setMeasurement(data);
}
}
测试结果:
this is observer.OneObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
使用Java API再写观察者的例子
实际上,Java API是支持观察者的,即上面例子中的Subject和Observer Java API中已经有所定义,即util包中的Observerable和Observer。那么我们可以不用写Subject和Observer了
上述例子使用Java API写出的效果是什么样子呢?我的实现是这样的:
先UML图就不画了,类截图如下:
写法比之前简化了不少
对比之前的类的截图,可以发现Subject和Observer不要写了,因为API已经写好了。
其次WeatherStation中要删掉无用代码,很多代码在Observable中已经帮我们写好了,废代码我在下面贴出的代码中标出了
最后各个Display的removeListener和update方法需要修改,因为Observer已经不是我们写的Observer,而是Java API中定义好的接口,我们要按接口来实现方法
剩下的都一样了,怎么样转变是不是很灵活。
我贴一下改动的部分,没有改动的就不贴了。
首先是被观察者实现类
public class WeatherStation extends Observable{
private WeatherBean mWeatherBean = null;
//private ArrayList<Observer> mObservers = new ArrayList<Observer>();//不需要了,Observable维护了一个Vector<Observer>数组
public void setMeasurement(WeatherBean data){
mWeatherBean = data;
measurementChanged();
}
private void measurementChanged(){
setChanged();
notifyObservers(mWeatherBean);
}
/*
不再需要自己写deleteObserver和registerObserver方法
Observable中addObserver(Observer ob) 和removeObserver(Observer ob)是现成的
@Override
public void deleteObserver(Observer ob) {
if(mObservers.contains(ob)){
mObservers.remove(ob);
}
}
public void registerObserver(Observer ob) {
if(!mObservers.contains(ob)){
mObservers.add(ob);
}
}*/
/*
notifyObservers也不需要了,Observable的notifyObservers也写好了
@Override
public void notifyObservers() {
super.notifyObservers();
for(Observer observer : mObservers){
observer.update(mWeatherBean);
}
}*/
}
其次是各个观察者,我贴两个
public class OneObserverDisplay implements Observer, DisplayElement {// 此处的Observer变成了java.util.Observer;
public WeatherStation mStation = null;
public OneObserverDisplay(WeatherStation station) {
this.mStation = station;
mStation.addObserver(this);
}
public void display(WeatherBean data) {
System.out.println("this is " + this.getClass().getName()
+ " getHumidity:" + data.getHumidity() + " getPressure:"
+ data.getPressure() + " getTemperature:"
+ data.getTemperature());
}
public void removeListener() {
this.mStation.deleteObserver(this);
}
public void update(Observable o, Object arg) {
if (arg instanceof WeatherBean) {
display((WeatherBean) arg);
}
}
}
public class TwoObserverDisplay implements Observer, DisplayElement {
public WeatherStation mStation = null;
public TwoObserverDisplay(WeatherStation station) {
this.mStation = station;
mStation.addObserver(this);
}
public void display(WeatherBean data) {
System.out.println("this is " + this.getClass().getName()
+ " getHumidity:" + data.getHumidity() + " getPressure:"
+ data.getPressure() + " getTemperature:"
+ data.getTemperature());
}
public void removeListener() {
this.mStation.deleteObserver(this);
}
public void update(Observable o, Object arg) {
if (arg instanceof WeatherBean) {
display((WeatherBean) arg);
}
}
}
剩下的代码和原来一样了。
输出结果如下:
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.OneObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
两种情况对比
1.代码简洁
从代码简洁性上看,实现Java API的方式完胜,省略了很多代码。
2.输出顺序
对比输出可以看出,输出顺序略微不同,可以看下被观察者的notifyObservers的实现:
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
可以看出,这个是倒序通知的。
3.setChange
API中的通知,有个private boolean changed = false;常量,在通知前,会判断其值,如果其是false是不会通知的。因此,每次通知或者数据变化都需要调用setChanged来确保发出通知。
4.变化适应性
其实Java API的设计有点问题,在Java编程经验中有一条:要面向接口编程而不是面向实现。
我们发现原先我们自己写的Subject是个接口,而JAVA API中的Observable是个实体类,这样如果我们的被观察者继承了Observable就无法继承其他类了,这就限制了被观察者实现的实体类的扩展和复用。
另外Java编程提倡多用组合少用继承。但是Observable将changed的控制方法大多设置为protected,这就意味着,即使我们把Observable的实体类组合到Observer实体类中,也无法控制changed因为protected只在本类及其子类可用。这违反了用组合少用继承,使得程序不够灵活,有时会限制程序的扩展。
总结,再看实例
因此,JAVA API的观察者模式的实现虽然减少了不少代码,但是也存在诸多扩展和灵活性问题。如果JAVA API提供的方式已经够用,那么就可以使用简便的JAVA API方式,但是如果觉得程序后期需要扩展的可能性大,那么最好还是不要闲麻烦,自己实现一下。对比之前的总结,我觉得还是当前这个版本比较通俗易懂。
在Java和Android系统中,观察者模式的例子很常见,比如Swing组件和Android Button控件的点击事件监听。Android 的广播和广播接收者,也是利用注册和解除注册来实现接收广播的,至少形式上很相似。
这就像从报社订报纸,每个用户可以选择定或者不定报纸。报社每月会遍历订阅用户列表,给他们寄去报纸;如果用户中途取消订阅,报社就删除该用户,下次不再寄去报纸。其他没有订阅的用户如果感兴趣了,也可以订阅,报社会在列表加入该用户,下次寄去报纸给该用户。