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照样运行,只不过可能显示不了信息了而已。
高内聚:
对象内部。
- 3、观察者模式
- 设计模式-3-观察者模式
- 设计模式(3)--观察者模式
- cocos2dx3.3 CCNotificationCenter观察者模式
- java设计模式(3)--观察者模式
- 设计模式(3)观察者模式
- 一天一个设计模式(3):观察者模式
- 学习模式----观察者模式(3)
- 设计模式:3)观察者模式
- 3、设计模式之观察者模式
- java设计模式(3) - 观察者模式(Observer)
- C#设计模式- 观察者模式(3)
- JAVA设计模式示例-3 观察者模式
- java设计模式(3)观察者模式
- 《java与模式》-3 观察者模式
- 观察者模式
- 观察者模式
- 观察者模式
- Parcelable接口实现
- JavaScript数字字符转数据类型
- 关于logcat日志
- POJ 1220 NUMBER BASE CONVERSION 高精度进制转换
- Garnter 安全培训
- 3、观察者模式
- 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子, 小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少?
- 大整数的加法运算
- 数据结构:大堆
- nginx手记-命令
- [LeetCode] Palindrome Partitioning
- 多线程与并发 概论
- linux下I/O缓冲
- 【C语言经典实例】-指向结构体的指针变量