观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化
1、Internet气象站项目
提供温度、气压和湿度接口
测量数据更新时需要通知给第三方
需要设计开放型API,便于其他第三方公司也能接入气象站获取数据
普通实现如下:
2、WeatherData类
测量数据更新时会调用类中的dataChange方法,dataChange方法中通过getTemperature、getPressure、getHumidity方法获得最新数据
public class WeatherData { private float mTemperature;//温度 private float mPreesure;//气压 private float mHumidity;//湿度 private CurrentConditions mCurrentConditions; public WeatherData(CurrentConditions currentConditions) { this.mCurrentConditions = currentConditions; } public float getTemperature() { return mTemperature; } public float getPreesure() { return mPreesure; } public float getHumidity() { return mHumidity; } //气象站数据变动后会调用该方法 public void dataChange() { mCurrentConditions.update(getTemperature(), getPreesure(), getHumidity()); } //模拟气象站数据变化,要调用dataChange方法 public void setData(float mTemperature, float mPreesure, float mHumidity) { this.mTemperature = mTemperature; this.mPreesure = mPreesure; this.mHumidity = mHumidity; dataChange(); } }
3、CurrentConditions类
某公司想接入气象站,展示气象数据,气象数据更新时,会调用CurrentConditions类的update方法来更新展示的气象数据
public class CurrentConditions { private float mTemperature; private float mPressure; private float mHumidity; public void update(float mTemperature, float mPressure, float mHumidity) { this.mTemperature = mTemperature; this.mPressure = mPressure; this.mHumidity = mHumidity; //假如更新后直接显示(也可以后期通过某条件后调用) display(); } public void display() { System.out.println("***Today mTemperature: " + mTemperature + "***"); System.out.println("***Today mPressure: " + mPressure + "***"); System.out.println("***Today mHumidity: " + mHumidity + "***"); } }
4、InternetWeather类
气象站注册应用类,启动后,通过setData模拟气象数据变化
public class InternetWeather { public static void main(String[] args) { CurrentConditions currentConditions; WeatherData weatherData; currentConditions = new CurrentConditions(); weatherData = new WeatherData(currentConditions); weatherData.setData(30, 150, 40); } }
5、引发的问题,如果再有其他公司想接入气象站,那么还要在WeatherData类中添加另一个公司的CurrentConditions类,同时还要停掉气象站服务,重新编译WeatherData类
同样如果有公司不想接入气象站了,那么还要去除对应类对象,停服务重新编译
6、使用观察者模式来解决此类问题,想接入气象站就去注册登记,不想接入就注册取消,登记的公司在气象数据变化后都会收到通知
代码实现:
public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); }
public interface Observer { void update(float mTemperature,float mPressure,float mHumidity); }
public class CurrentConditions implements Observer { private float mTemperature; private float mPressure; private float mHumidity; public void update(float mTemperature, float mPressure, float mHumidity) { this.mTemperature = mTemperature; this.mPressure = mPressure; this.mHumidity = mHumidity; display(); } public void display(){ System.out.println("***Today mTemperature: " + mTemperature + "***"); System.out.println("***Today mPressure: " + mPressure + "***"); System.out.println("***Today mHumidity: " + mHumidity + "***"); } }
public class ForcastConditions implements Observer { private float mTemperature; private float mPressure; private float mHumidity; public void update(float mTemperature, float mPressure, float mHumidity) { this.mTemperature = mTemperature; this.mPressure = mPressure; this.mHumidity = mHumidity; display(); } public void display(){ System.out.println("***Tomorrow mTemperature: " + mTemperature + "***"); System.out.println("***Tomorrow mPressure: " + mPressure + "***"); System.out.println("***Tomorrow mHumidity: " + mHumidity + "***"); } }
public class WeatherData implements Subject { private float mTemperature;//温度 private float mPreesure;//气压 private float mHumidity;//湿度 private List<Observer> mObservers; public WeatherData() { mObservers = new ArrayList<Observer>(); } public float getmTemperature() { return mTemperature; } public void setmTemperature(float mTemperature) { this.mTemperature = mTemperature; } public float getmPreesure() { return mPreesure; } public void setmPreesure(float mPreesure) { this.mPreesure = mPreesure; } public float getmHumidity() { return mHumidity; } public void setmHumidity(float mHumidity) { this.mHumidity = mHumidity; } //气象站数据变动后会调用该方法 public void dataChange() { notifyObservers(); } //模拟气象站数据变化,要调用dataChange方法 public void setData(float mTemperature, float mPreesure, float mHumidity) { this.mTemperature = mTemperature; this.mPreesure = mPreesure; this.mHumidity = mHumidity; dataChange(); } public void registerObserver(Observer observer) { mObservers.add(observer); } public void removeObserver(Observer observer) { if (mObservers.contains(observer)) mObservers.remove(observer); } public void notifyObservers() { for (int i = 0; i < mObservers.size(); i++) { mObservers.get(i).update(getmTemperature(), getmPreesure(), getmHumidity()); } } }
public class InternetWeather { public static void main(String[] args) { CurrentConditions currentConditions; ForcastConditions forcastConditions; WeatherData weatherData; currentConditions = new CurrentConditions(); forcastConditions = new ForcastConditions(); weatherData = new WeatherData(); weatherData.registerObserver(currentConditions); weatherData.registerObserver(forcastConditions); weatherData.setData(30, 150, 40); System.out.println("````````````````````"); weatherData.removeObserver(forcastConditions); weatherData.setData(40,160,50); } }
7、Java内置观察者(允许发出通知时直接推送给观察者,也可以通知观察者后让观察者来拉取数据)
Java内置观察者使用的是Observable类(不是接口,作用类似实现Subject接口的类),和Observer接口
注意,在观察者通知被观察者前要调用setChange方法
public class CurrentConditions implements Observer { private float mTemperature; private float mPressure; private float mHumidity; public void update(Observable o, Object arg) { this.mTemperature=((Data)arg).mTemperature; this.mPressure=((Data)arg).mPressure; this.mHumidity=((Data)arg).mHumidity; display(); } public void display(){ System.out.println("***Today mTemperature: " + mTemperature + "***"); System.out.println("***Today mPressure: " + mPressure + "***"); System.out.println("***Today mHumidity: " + mHumidity + "***"); } }
public class ForcastConditions implements Observer { private float mTemperature; private float mPressure; private float mHumidity; public void update(Observable o, Object arg) { this.mTemperature=((WeatherData.Data)arg).mTemperature; this.mPressure=((WeatherData.Data)arg).mPressure; this.mHumidity=((WeatherData.Data)arg).mHumidity; display(); } public void display(){ System.out.println("***Tomorrow mTemperature: " + mTemperature+1 + "***"); System.out.println("***Tomorrow mPressure: " + mPressure+1 + "***"); System.out.println("***Tomorrow mHumidity: " + mHumidity+1 + "***"); } }
public class WeatherData extends Observable { private float mTemperature;//温度 private float mPressure;//气压 private float mHumidity;//湿度 public float getmTemperature() { return mTemperature; } public void setmTemperature(float mTemperature) { this.mTemperature = mTemperature; } public float getmPreesure() { return mPressure; } public void setmPreesure(float mPreesure) { this.mPressure = mPreesure; } public float getmHumidity() { return mHumidity; } public void setmHumidity(float mHumidity) { this.mHumidity = mHumidity; } //气象站数据变动后会调用该方法 public void dataChange() { this.setChanged();//一定要先调用这个方法,此方法是灵活设置是否通知观察者(如数据变化不大时不需要通知) // this.notifyObservers();//通知观察者,来拉取数据 this.notifyObservers(new Data(getmTemperature(),getmPreesure(),getmHumidity())); } //模拟气象站数据变化,要调用dataChange方法 public void setData(float mTemperature, float mPressure, float mHumidity) { this.mTemperature = mTemperature; this.mPressure = mPressure; this.mHumidity = mHumidity; dataChange(); } public class Data { public float mTemperature;//温度 public float mPressure;//气压 public float mHumidity;//湿度 public Data(float mTemperature, float mPrsesure, float mHumidity) { this.mTemperature = mTemperature; this.mPressure = mPressure; this.mHumidity = mHumidity; } } }
public class InternetWeather { public static void main(String[] args) { CurrentConditions currentConditions; ForcastConditions forcastConditions; WeatherData weatherData; currentConditions = new CurrentConditions(); forcastConditions = new ForcastConditions(); weatherData = new WeatherData(); //注意注册顺序,最后注册的先通知 weatherData.addObserver(currentConditions); weatherData.addObserver(forcastConditions); weatherData.setData(30, 150, 40); System.out.println("````````````````````"); weatherData.deleteObserver(forcastConditions); weatherData.setData(40,160,50); } }