观察者模式(Obserber Pattern):定义对象之间一对多的依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式主要用来处理一对多的数据更新和传输的关系。易于对实现对象的控制和更新。观察者模式主要分为两部分,一部分称为主题(Subject)类似于报纸的出版商,另一部分称为观察者(Observer)类似于报纸的订购者。这个模式就相当于出版商和订购者的关系,当订购者需要得到数据时就会在出版商这里注册,以通知出版商,取得获取数据的资格,一旦有数据更新的时候订购者就像读报纸一样得到数据,这样报社不需要去照顾订购者的数量和实现方法。只需要对已注册的跟新即可。
举个例子
接到一个项目,用来做气象监测,分为两个部分,一个是WeatherData对象:追踪监测的气象数据,并跟新布告板。另一个是布告板:通过终端(手机、电视、电脑)显示当前天气状况给用户。
分析需求:
已知:1)WeatherData类有三个get方法,取得三个测量值:温度、湿度和气压。
2)有一个update方法。
3)一旦WeatherData有新的数据产生时,就会立即向布告板更新。
4)需要可扩展,因为终端不同,需要能让其他开发人员建立布告板。
这样就可以来设计了,通过上面对观察者的介绍和针对接口的OO原则。
定义主题接口:
//主题接口
public interface Subject{
//注册观察者方法
public void registerObserver(Observer ob);
//删除观察者方法
public void removeObserver(Observer ob);
//通知观察者方法
public void notifyObserver(Observer ob);
}
观察者接口:
//观察者接口
public interface Observer{
//更新数据方法
public void update(float temp,float humidity,float pressure);
}
发布接口:
//发布接口
public interface DisplayElement{
public void display();
}
开始实现:
首先实现WeatherData
//通过Subject接口实现WeatherData
public class WeatherData implements Subject{
//定义一个列表,用来盛放注册的观察者
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers=new ArrayList();
}
public void registerObserver(Observer ob){
observers.add(ob);
}
public void removeObserver(Observer ob){
//返回此列表中首次出现的ob的索引,如果此列表不包含元素,则返回 -1。
if(observers.indexOf(ob)>=0)
observers.remove(ob);
}
public void notifyObserver(Observer ob){
for(int i=0;i<observers.size();i++){
Observer observer=(Observer) observers.get(i);
observer.update(temperature,humidity,pressure);
}
}
//当WeatherData数据改变时,才会更新。
public void measurementsChanged(){
notifyObserver();
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
}
通过Observer实现布告板:
//通过Observer接口实布告板
public class BillBoard implements Observer,DisplayElement(){
private float temperature;
private float humidity;
private float pressure;
public BillBoard(Subject WeatherData){
this.WeatherData=WeatherData;
WeatherData.removeObserver(this);
}
public void update(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
/*
这个方法由开发人员重写
*/
@Override
public void display(){
}
}
这样整个设计基本上就OK了,下面写个test类来测试一下:
//测试类
public class test{
public static void main(String args[]){
WeatherData watherData=new WeatherData();
BillBoard billBoard=new BillBoard();
//不止这一个公告板,如果有的话在下面继续加
weatherData.setMeasurements(22,55,66);
weatherData.setMeasurements(5,96,47);
}
}
不出错的话这个任务基本上就完成了。
到目前为止,已经从无到有的完成了Observer Pattern。关于观察者,主题只知道观察者实现的Observer接口,不需要知道其他的任何细节。 任何时候都可以增加新的观察者,只需要在主题ArrayList里面注册就可以。 有新的类型观察者出现时,主题代码还是不需要修改,主题代码不在乎别的,只有一个功能就是给已注册的观察者发送数据。 独立的复用主题或者观察者,二者独立,只需要遵守接口,改变不会产生影响。
多说一点,上面的这种设计实际上是由主题进行一个类似于“推”的动作,它把数据推送到各个观察者。观察者是被动接收数据的对象。实际上Java有一个内置的观察者模式,实现的是另一种不同的思路。说白了是“拉”,观察者从主题里去拉取自己需要的数据,去按照自己的需求主动获取数据。位于java.util包里。