观察者模式
一.引子
在许多设计中,经常涉及多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化, 例如:1.京东上某个商品暂时没货,提示用户关注后到货通知,这个暂时无货的商品是被观察者,点击关注这个商品的用户就是观察者。 2、老师针对成绩在60分以下的同学定期发送最新的考题分析邮件,每轮考试下来都会有不及格的同学,由不及格变为及格的同学自动从邮件列表里移除,新的不及格的同学会被加进邮件列表里。在这样的情况下就可以使用观察者模式。
二、观察者模式
- 观察者模式的概念:
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。依赖的对象为Observer,Subject通知Observer变化。
2.模式的结构:
观察者模式的结构中包含四种角色:
(1) 主题(subject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加,删除观察者以及通知观察者更新数据的方法、
(2) 观察者(Observer):观察者是一个借口,该接口规定了具体观察者用来更新数据的方法。
(3) 具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
(4) 具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题的主题接口变量,一遍具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己称为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。
三.范例
气象站现在需要我们做一个公告项目,通过感应状态获取的数据将气象状态进行展示。

一般的设计方案中,我们在WeatherData对象中,存放他们的引用,然后当我的值变化的时候,依次调用公告板的change() 函数通知变化。但这样的方案却又很多问题:
- 没有面相接口编程的思想
- 如果还有新的广告板,需要修改WeatherData
- 如果通知的信号(温度,湿度,气压)再增加一个参数,那么dataChange需要修改,所有的广告板都需要修改。

因此,我们用观察者模式重新设计方案。

在这个项目中,我们其实可以看作广告板为(观察者Observer),时刻关注着 WeatherData (主题Subject) 的变化,如果有变化,则通知广告板更新。
首先提供主题和观察者两个接口:
//主题Subjet接口 public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
//观察者Observer接口 public interface Observer { public void update(float mTemperatrue,float mPressure,float mHumidity); }
然后创建具体主题WeatherData(ConcreteSubject)和具体观察者广告板(ConcreteObserver)
//具体主题 public class WeatherDataSt implements Subject{ private float mTemperatrue; private float mPressure; private float mHumidity; //创建Lisr,存放观察者引用 private ArrayList<Observer> mObservers; public WeatherDataSt() { mObservers=new ArrayList<Observer>(); } public float getTemperature() { return mTemperatrue; } public float getPressure() { return mPressure; } public float getHumidity() { return mHumidity; } public void dataChange() { notifyObservers(); } public void setData(float mTemperatrue,float mPressure,float mHumidity) { this.mTemperatrue=mTemperatrue; this.mPressure=mPressure; this.mHumidity=mHumidity; dataChange(); } @Override public void registerObserver(Observer o) { // TODO Auto-generated method stub mObservers.add(o); } @Override public void removeObserver(Observer o) { // TODO Auto-generated method stub if(mObservers.contains(o)) {mObservers.remove(o);} } @Override public void notifyObservers() { // TODO Auto-generated method stub for(int i=0,len=mObservers.size();i<len;i++) { mObservers.get(i).update(getTemperature(), getPressure(), getHumidity()); } } }
//具体观察者广告板1 public class CurrentConditions implements Observer { private float mTemperatrue; private float mPressure; private float mHumidity; @Override public void update(float mTemperatrue, float mPressure, float mHumidity) { // TODO Auto-generated method stub this.mHumidity = mHumidity; this.mPressure = mPressure; this.mTemperatrue = mTemperatrue; display(); } public void display() { System.out.println("***Today mTemperatrue:" + mTemperatrue + "***"); System.out.println("***Today mPressure:" + mPressure + "***"); System.out.println("***Today mHumidity:" + mHumidity + "***"); } }
//观察板2 public class ForcastConditions implements Observer{ private float mTemperatrue; private float mPressure; private float mHumidity; @Override public void update(float mTemperatrue, float mPressure, float mHumidity) { // TODO Auto-generated method stub this.mTemperatrue=mTemperatrue; this.mPressure=mPressure; this.mHumidity=mHumidity; display(); } public void display() { System.out.println("**明天温度:"+(mTemperatrue+Math.random())+"**"); System.out.println("**明天气压:"+(mPressure+10*Math.random())+"**"); System.out.println("**明天湿度:"+(mHumidity+Math.random())+"**"); } }
//客户端调用,主方法 public class InternetWeather { public static void main(String[] args) { CurrentConditions mCurrentConditions; ForcastConditions mForcastConditions; WeatherDataSt mWeatherDataSt; mWeatherDataSt=new WeatherDataSt(); mCurrentConditions=new CurrentConditions(); mForcastConditions=new ForcastConditions(); mWeatherDataSt.registerObserver(mCurrentConditions); mWeatherDataSt.registerObserver(mForcastConditions); mWeatherDataSt.setData(30, 150, 40); mWeatherDataSt.removeObserver(mCurrentConditions); mWeatherDataSt.setData(40, 250, 50); }
这样,我们的观察者模式就完成了,这样做的好处:
- 首先现在 WeatherData 可以添加任意多个观察者
- 主题和观察者解耦,类中保持的是接口对象。
- 观察主题的具体内容解耦了,WeatherData获取的信息,和Observer关心的数据分开。
四、JAVA中自带的观察者模式
Java 的 java.util包 中自带了 Observer接口和Observable类和我们前面的 Subject接口和Observer接口很像。
我们来看看使用Java的观察者模式,重写上面的例子:
public class WeatherData extends Observable{ private float mTemperatrue; private float mPressure; private float mHumidity; public float getTemperature() { return mTemperatrue; } public float getPressure() { return mPressure; } public float getHumidity() { return mHumidity; } public void dataChange() { this.setChanged(); this.notifyObservers(new Data(getTemperature(),getPressure(),getHumidity())); } public void setData(float mTemperatrue,float mPressure,float mHumidity) { this.mTemperatrue=mTemperatrue; this.mPressure=mPressure; this.mHumidity=mHumidity; dataChange(); } public class Data { public float mTemperatrue; public float mPressure; public float mHumidity; public Data(float mTemperatrue,float mPressure,float mHumidity) { this.mTemperatrue=mTemperatrue; this.mPressure=mPressure; this.mHumidity=mHumidity; } } }
public class CurrentConditions implements Observer { private float mTemperatrue; private float mPressure; private float mHumidity; @Override public void update(Observable arg0, Object arg1) { // TODO Auto-generated method stub this.mTemperatrue=((Data)(arg1)).mTemperatrue; this.mPressure=((Data)(arg1)).mPressure; this.mHumidity=((Data)(arg1)).mHumidity; display(); } public void display() { System.out.println("***Today mTemperatrue:" +mTemperatrue+"***"); System.out.println("***Today mPressure:" +mPressure+"***"); System.out.println("***Today mHumidity:" +mHumidity+"***"); } }
浙公网安备 33010602011771号