3、观察者模式

来源:互联网 发布:尤里的复仇数据修改 编辑:程序博客网 时间:2024/05/16 19:56

观察者模式

常用于:对象间多对一依赖。被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化。

生活中的例子对应:


1>订牛奶
奶站--Subject:是一个接口,实现功能:注册、移除、通知
用户--Observer:也是一个接口,实现功能:接收输入



2>气象播报

气象站--Subject

公告牌--Observer


以气象播报为例:

vs1:什么都不用,想当然直接写出来的情况:


提供数据接口的气象站 WeatherData.java :

public class WeatherData {private float mTemperature;private float mPressure;private float mHumidity;private CurrentConditions mCurrentConditions = null;public WeatherData(CurrentConditions currentConditions){mCurrentConditions = currentConditions;}public float getTemperature() {return mTemperature;}public float getPressure() {return mPressure;}public float getHumidity() {return mHumidity;}public void dataChanged(){mCurrentConditions.update(getTemperature(), getPressure(), getHumidity());}public void setData(float temperature, float pressure, float humidity){mTemperature = temperature;mPressure = pressure;mHumidity = humidity;dataChanged();}}



接收气象数据的公告牌 CurrentConditions.java:

public class CurrentConditions {private float mTemperature;private float mPressure;private float mHumidity;public void update(float temperature, float pressure, float humidity){mTemperature = temperature;mPressure = pressure;mHumidity = humidity;display();}public void display(){System.out.println("Today's temperature:"+ mTemperature);System.out.println("Today's pressure:"+ mPressure);System.out.println("Today's humidity:"+ mHumidity);}}



写个类(WeatherTest.java)测试一下:

public class WeatherTest {public static void main(String[] args) {CurrentConditions mCurrentConditions; WeatherData mWeatherData;mCurrentConditions = new CurrentConditions();mWeatherData = new WeatherData(mCurrentConditions);mWeatherData.setData(30, 150, 40);}}


运行结果:

Today's temperature:30.0Today's pressure:150.0Today's humidity:40.0

很显然可以运行,但是当增多一个接收信息的公告牌时,改动很麻烦(flag)。


vs2:采用装饰者模式


先定义一个接口 Subject.java:

public interface Subject {public void registerObserver(Observer o);public void removeObserver(Observer o);public void notifyObservers();}
创建注册、移除、通知三个方法


再定义一个接口 Observer.java:

public interface Observer {public void update(float temperature, float pressure, float humidity);}
创建更新数据的update()方法,接收传进来的数据。


提供数据接口的气象站 WeatherData.java 很明显要实现 Subject.java 接口:

public class WeatherData implements Subject {private float mTemperature;private float mPressure;private float mHumidity;private ArrayList<Observer> mObservers = null;public WeatherData(){mObservers = new ArrayList<Observer>();}public float getTemperature() {return mTemperature;}public float getPressure() {return mPressure;}public float getHumidity() {return mHumidity;}public void dataChanged(){notifyObservers();}public void setData(float temperature, float pressure, float humidity){mTemperature = temperature;mPressure = pressure;mHumidity = humidity;dataChanged();}@Overridepublic void registerObserver(Observer o) {mObservers.add(o);}@Overridepublic void removeObserver(Observer o) {mObservers.remove(o);}@Overridepublic void notifyObservers() {for(int i = 0; i < mObservers.size(); i++){//这里有两种方式通知://vs1:直接把参数传过去mObservers.get(i).update(getTemperature(), getPressure(), getHumidity());//vs2:只是通知观察者们有新消息了,需要什么参数观察者们自己去取(更适合扩展),取回来的数据再进行解析}}}


而作为接收气象数据的公告牌有两个,两个都要实现Observer.java接口:

CurrentConditions.java:

public class CurrentConditions implements Observer {private float mTemperature;private float mPressure;private float mHumidity;@Overridepublic void update(float temperature, float pressure, float humidity) {mTemperature = temperature;mPressure = pressure;mHumidity = humidity;display();}public void display(){System.out.println("Today's temperature:"+ mTemperature);System.out.println("Today's pressure:"+ mPressure);System.out.println("Today's humidity:"+ mHumidity);}}


ForecastConditions.java:

public class ForecastConditions implements Observer {private float mTemperature;private float mPressure;private float mHumidity;@Overridepublic void update(float temperature, float pressure, float humidity) {mTemperature = temperature;mPressure = pressure;mHumidity = humidity;display();}public void display(){System.out.println("明天温度:"+ (mTemperature+Math.random()));System.out.println("明天气压:"+ (mPressure+Math.random()));System.out.println("明天湿度:"+ (mHumidity+Math.random()));}}


这样一来,就用装饰者模式重新实现了一下这个气象播报例子,写个类(WeatherTest2.java)测试一下:

public class WeatherTest2 {public static void main(String[] args) {CurrentConditions mCurrentConditions;ForecastConditions mForecastConditions;WeatherData mWeatherData;mWeatherData = new WeatherData();mCurrentConditions = new CurrentConditions();mForecastConditions = new ForecastConditions();mWeatherData.registerObserver(mCurrentConditions);mWeatherData.registerObserver(mForecastConditions);mWeatherData.setData(30, 150, 40);}}

运行结果:

Today's temperature:30.0Today's pressure:150.0Today's humidity:40.0明天温度:30.641740621608005明天气压:150.4129630982695明天湿度:40.283201107445485


改动一下WeatherTest2.java再测试一下:

public class WeatherTest2 {public static void main(String[] args) {CurrentConditions mCurrentConditions;ForecastConditions mForecastConditions;WeatherData mWeatherData;mWeatherData = new WeatherData();mCurrentConditions = new CurrentConditions();mForecastConditions = new ForecastConditions();mWeatherData.registerObserver(mCurrentConditions);mWeatherData.registerObserver(mForecastConditions);mWeatherData.removeObserver(mCurrentConditions);mWeatherData.setData(35, 160, 50);}}

运行结果:

明天温度:35.69158101675214明天气压:160.32248284833398明天湿度:50.3754990869303

嗯,全部都运行成功了。


vs3:Java内置观察者

其实,还有第三种方式,更简便的方式,不过有一点局限性就是了。


按我自己的理解,如果可以用Java内置观察者的话,就直接用。
如果不可以的话(比如要继承其他的第三方接口,又要继承Observable时),就自己定义吧。


Java内部有一个 Java内置观察者,使用了观察者模式。

Subject对应的是Observable,也实现了注册、移除、通知三个功能。

Observer对应的还是Observer,也还是一个接口。(之所以还是接口,是因为观察者可能会选择直接通过参数传递过来数据,也可能仅仅想被通知一下,然后自己去取数据)

唯一不同的是Observable是一个类而不是接口,所以观察者需要extends(而不是implements)Observable(java.util.Observable)类


提供数据接口的气象站 WeatherData.java,继承了Observable类:

public class WeatherData extends Observable {private float mTemperature;private float mPressure;private float mHumidity;public float getTemperature() {return mTemperature;}public float getPressure() {return mPressure;}public float getHumidity() {return mHumidity;}public void dataChanged(){//使通知观察者时更具灵活性(比如想实现超过0.5度的变化才通知时,就可以在超过0.5度的逻辑中写下这句代码)this.setChanged();//vs1:直接推送信息给观察者this.notifyObservers(new Data(getTemperature(), getPressure(), getHumidity()));//vs2:仅仅通知观察者,需要什么让观察者自己去取}public void setData(float temperature, float pressure, float humidity){mTemperature = temperature;mPressure = pressure;mHumidity = humidity;dataChanged();}public class Data {public float mTemperature;public float mPressure;public float mHumidity;public Data(float temperature, float pressure, float humidity){mTemperature = temperature;mPressure = pressure;mHumidity = humidity;}}}



很明显接收气象数据的另两个公告牌就要实现Observer接口了:

CurrentConditions.java:

public class CurrentConditions implements Observer {private float mTemperature;private float mPressure;private float mHumidity;@Overridepublic void update(Observable o, Object arg) {//Object是notifyObservers()方法传递过来的参数,传过来时事Data类,所以现在也要转换成Data类this.mTemperature = ((Data)(arg)).mTemperature;this.mPressure = ((Data)(arg)).mPressure;this.mHumidity = ((Data)(arg)).mHumidity;display();}public void display(){System.out.println("Today's temperature:"+ mTemperature);System.out.println("Today's pressure:"+ mPressure);System.out.println("Today's humidity:"+ mHumidity);}}


ForecastConditions.java:

public class ForecastConditions implements Observer {private float mTemperature;private float mPressure;private float mHumidity;@Overridepublic void update(Observable o, Object arg) {//Object是notifyObservers()方法传递过来的参数,传过来时事Data类,所以现在也要转换成Data类this.mTemperature = ((Data)(arg)).mTemperature;this.mPressure = ((Data)(arg)).mPressure;this.mHumidity = ((Data)(arg)).mHumidity;display();}public void display(){System.out.println("Tomorrow's temperature:"+ (mTemperature+1));System.out.println("Tomorrow's pressure:"+ (mPressure+1));System.out.println("Tomorrow's humidity:"+ (mHumidity+1));}}



没错,这样就写完了,写个类(WeatherTest3.java)测试一下吧:

public class WeatherTest3 {public static void main(String[] args) {CurrentConditions mCurrentConditions;ForecastConditions mForecastConditions;WeatherData mWeatherData;mWeatherData = new WeatherData();mCurrentConditions = new CurrentConditions();mForecastConditions = new ForecastConditions();mWeatherData.addObserver(mCurrentConditions);mWeatherData.addObserver(mForecastConditions);mWeatherData.setData(30, 150, 40);mWeatherData.deleteObserver(mCurrentConditions);mWeatherData.setData(35, 150, 60);}}


运行结果:

Tomorrow's temperature:31.0Tomorrow's pressure:151.0Tomorrow's humidity:41.0Today's temperature:30.0Today's pressure:150.0Today's humidity:40.0Tomorrow's temperature:36.0Tomorrow's pressure:151.0Tomorrow's humidity:61.0

咦,这里发现一个问题,我先加了一个CurrentConditions类的Observer,再加了一个ForecastConditions类的Observer,为什么先输出的是Tomorrow的数据(ForecastConditions类的)呢?


原来自己定义Subject、Observer的话,是先注册先通知,先进先出原则
Java内置观察者刚好相反,是先注册后通知,先进后出原则。


所以这里才会反过来显示。


气象站Subject有新数据过来了,调用顺序:dataChange()-->notifyObservers()-->update() [update observers中相关数据]
公告牌Observer


嗯,至此,例子就全部整理完了。


下面简单说下观察者模式的特点:

特点么,

低耦合:
对象之间。
只需要知道要调用的对方的那个函数即可,其他诸如怎么实现的,数据怎么传过来的都不用管。
打个比方:Subject挂了,Observer照样运行,只不过可能显示不了信息了而已。


高内聚:
对象内部。






0 0
原创粉丝点击