初识观察者模式
观察者模式原理
以一个气象站平台项目为例。气象站平台通过仪器收集各种数据包括温度湿度等等,之前的气象站平台收集到这些数据后直接在平台上进行发布即可,但后来他们意识到自己平台的影响力不够大,所以他们决定引入第三方平台,第三方平台可以利用更优质的算法整合气象站平台收集的数据得到穿衣指数等新内容 。
气象站需要提供:
①温度气压湿度等数据的接口
②测量数据更新时要实时通知第三方
③需要设计开放性API,便于其他第三方公司也能方便地接入或退出从气象站获取数据
气象站设计的WeatherData类包括以下几个方法:
getTemperature()//获取温度
getHumidity()//获取湿度
getPressure()//获取气压
dataChange()//如果获取到的信息改变时通知平台使用以上函数获取信息
一个通常的设计方案
CurrentCondition作为信息公告板展示实时信息,通过dataChanged()方法调用update()方法并将信息display()出来。
存在的问题:
1)其他第三方公司接入气象站获取数据的问题:每增加一个新的公司都要增加一个CurrentConditions对象,会导致dataChange()函数每次都要重新编译一次。
2)无法在运行时动态地添加第三方
在项目中公告板往往是独立的进程一直保持运行,不能经常性重新编译,因此这个方案的扩展性很不好。
思考:
问题中变化和不变的部分。变化部分包括不同的公告板,dataChange()函数也随着不同的公告板进行变化。
类比:
观察者模式就像订牛奶业务:去牛奶公司去订购牛奶之后就会每天准时送达牛奶,如果有一天不需要了,就去解除业务牛奶公司就会停止供给。
1)奶站,Subject:登记注册。移除和通知
2)用户,Observer:接收输入
观察者模式:定义对象之间的一种一对多伊利爱关系,似的每一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。其中被依赖的对象叫做Subject,依赖的对象叫Observer,Subject通知Observer变化.
观察者模式设计
观察者模式示例
根据上述类图设计Observer和Subject接口
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyrObserver();
}
第一个Observer类CurrentConditions
import observer.Observer;
public class CurrentConditions implements Observer{
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
// TODO Auto-generated method stub
this.temperature=temperature;
this.pressure=pressure;
this.humidity=humidity;
display();
}
public void display() {
System.out.println("今日温度"+temperature+"°C");
System.out.println("今日气压"+pressure+"pa");
System.out.println("今日湿度"+humidity+"湿");
}
}
可以再写一个ForcastConditions类作为第二个Observer,这样方便更好地理解观察者模式。
WeatherData类的设计:
在继承Subject接口的基础上还要定义一个链表作为存储Observer的数据结构,其中Subject接口的方法这样实现:
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
myObserver.add(o);
}
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
if(myObserver.contains(o)) {
myObserver.remove(o);
}
}
@Override
public void notifyrObserver() {
// TODO Auto-generated method stub
for(int i=0,len=myObserver.size();i<len;i++) {
myObserver.get(i).update(getTemperature(),getHumidity(), getPressure());
}
}
这样有个问题就是在notifyObserve的实现中,我们调用了update()方法并传递了三个参数,如果接下来的需求中我们需要增加新的参数,而其他Observer也许并不需要,就很不利于扩展性。在接下来要讲到的java内置观察者中解决了这个问题。内置观察者有两种数据传递方式,一种是上面这样直接定义好接口把数据传送过去,还有一种是当数据发生改变时通知所有Observer,Observer根据自己需要选取数据。
java内置观察者
Subject:Observable(是一个类,不是接口。已经实现了注册移除和通知使用时直接继承就可以了)
Observer:Observer