观察者模式
目录
一、从一个气象监测应用开始
某个公司希望建立一个应用,有三种布告板,分别显示目前的天气状况、气象统计及简单的预告,当WeatherObject对象获得最新的测量数据时,三种布告板可以实时更新。
WeatherData对象知道如何跟气象站联系,以便取得更新的数据。WeatherData对象会随即更新三个布告板的显示。我们的工作就是建立一个应用,利用WeatherData对象取得数据,并且更新三个布告板。
WeatherDta类
我们的工作就是要实现measurementsChanged(),好让它更新目前状况、气象统计、天气预报的布告板。
1.1、一个错误的示范
1.2、这样设计的问题
1、针对具体实现编程,并非针对接口编程
2、对于每个新的布告板,都必须修改代码
3、无法在运行时动态增加、删除布告板
4、布告板没有实现一个共同的接口
5、未封装改变的部分
6、侵犯了WeatherData类的封装
二、基于观察者模式的改造
2.1、什么是观察者模式
2.1.1、引入
首先先理解一下报纸跟杂志的订阅是什么关系:
观察者模式就是等于出版者+订阅者,出版者为“主题”,订阅者为“观察者”,如下图示:
当要向观察者对象中添加新的观察者时,只需要告诉主题
如果观察者对象中的观察者需要删除自己,则告诉主题自己的请求,然后从观察者对象中除名,此后如果主题中数据发生改变,则该观察者收不到
2.1.2、观察者模式的定义
2.1.3、观察者模式的类图
2.1.4、观察者模式的好处
观察者模式中观察者和主题之间松耦合,大大降低了两者之间的依赖。
关于观察者的一切,主题只需要知道观察者实现了某个接口(Observer接口)。主题不需要知道观察者的具体类是谁、做了什么。
当我们需要添加新的观察者时,因为主题只唯一依赖的东西就是一个实现了Observer接口的对象列表,所以我们在运行时就可以添加新的观察者或者取代其他观察者或者删除观察者,主题不会收到影响。
有新的观察者出现时,主题的代码不需要改变。需要做的就是在新的类中实现观察者接口,然后注册为观察者即可。主题不会在乎别的,它只会发生通知给所有实现了观察者接口的对象。
2.1.5、观察者模式中的设计原则
a、为了交互对象之间的松耦合设计而努力
2.2、基于观察者模式的改进
2.2.1、气象站的类图
2.2.2、具体代码实现
Observer接口
package com.java.hexter.internetweather.observer;
public interface Observer { //定义接口,用于添加观察者时,实现观察者
public void update(float mTemperatrue,float mPressure,float mHumidity);
}
Subject接口
package com.java.hexter.internetweather.observer;
public interface Subject { //定义主题接口,用于注册、删除、通知观察者
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
主题类的实现
package com.java.hexter.internetweather.mode;
import java.util.ArrayList;
import com.java.hexter.internetweather.observer.Observer;
import com.java.hexter.internetweather.observer.Subject;
public class WeatherDataSt implements Subject{
private float mTemperatrue;
private float mPressure;
private float mHumidity;
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());
}
}
}
观察者类的实现
package com.java.hexter.internetweather.mode;
import com.java.hexter.internetweather.observer.Observer;
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 + "***");
}
}
package com.java.hexter.internetweather.mode;
import com.java.hexter.internetweather.observer.Observer;
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())+"**");
}
}
测试类
package com.java.hexter.internetweather.mode;
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);
}
}
三、Java中内置的观察者模式
3.1、Java内置的观察者模式
Java API中有内置的观察者模式,java.util包中包含最基本的Observer接口和Observable类,两个接口和类与Subject接口和Observer接口很相似。使用时只需“推”或者"拉"的方式传送数据。
具体的类图如下
3.2、如何使用内置的观察者模式
步骤一:将对象变成观察者
对象实现观察者接口(java.util.Observer),然后调用Observable对象的addObserver()方法,如果需要删除观察者,则调用deleteObserver()方法即可。
步骤二:观察者发送通知
setChanged()方法
步骤三:观察者接收通知
3.3、基于JAVA内置观察者模式的气象站改造
具体代码如下:
主题类
package com.java.hexter.internetweather.jv;
import java.util.Observable;
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;
}
}
}
观察者类
package com.java.hexter.internetweather.jv;
import java.util.Observable;
import java.util.Observer;
import com.java.hexter.internetweather.jv.WeatherData.Data;
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+"***");
}
}
package com.java.hexter.internetweather.jv;
import java.util.Observable;
import java.util.Observer;
import com.java.hexter.internetweather.jv.WeatherData.Data;
public class ForcastConditions 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("***Tomorrow mTemperatrue:" +(mTemperatrue+1)+"***");
System.out.println("***Tomorrow mPressure:" +(mPressure+1)+"***");
System.out.println("***Tomorrow mHumidity:" +(mHumidity+1)+"***");
}
}
测试类
package com.java.hexter.internetweather.jv;
public class InternetWeather {
public static void main(String[] args) {
CurrentConditions mCurrentConditions;
ForcastConditions mForcastConditions;
WeatherData mWeatherData;
mCurrentConditions=new CurrentConditions();
mForcastConditions=new ForcastConditions();
mWeatherData=new WeatherData();
mWeatherData.addObserver(mCurrentConditions);
mWeatherData.addObserver(mForcastConditions);
mWeatherData.setData(30, 150, 40);
mWeatherData.deleteObserver(mCurrentConditions);
mWeatherData.setData(35, 150, 60);
}
}