设计模式2-观察者模式

242 阅读4分钟

1.场景问题解决

气象站场景,合作方有提供气象数据变化的接口,然后需要气象数据变更的时候及时同步到自己的布告板上.

1.1 场景描述

  • Internet气象站项目,需要实现一下功能:
    1)提供温度、气压和湿度的接口
    2)测量数据更新时需时时通知给第三方   
    3)需要设计开放型API,便于其他第三方公司也能接入气象站获取数据   

02观察者模式-1.png

1.2 OO设计

1.2.1 设计类图

02观察者模式-2.png

1.2.2 代码


/**
 * 公告板--显示装置
 */
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("CurrentConditions-->***Today mTemperature: "+mTemperature+"***");
		System.out.println("CurrentConditions-->***Today mPressure: "+mPressure+"***");
		System.out.println("CurrentConditions-->***Today mHumidity: "+mHumidity+"***");
	}
}



/**
 * 天气指数产生类(气象站)--并通知显示装置
 */
public class WeatherData {
	
	private float mTemperatrue;
	private float mPressure;
	private float mHumidity;
	private CurrentConditions mCurrentConditions;

	/**
	 * 构造方法
	 */
	public WeatherData(CurrentConditions mCurrentConditions){
		this. mCurrentConditions= mCurrentConditions;
	}
	
	public void setData(float mTemperature,float mPressure,float mHumidity){
		this.mTemperatrue=mTemperature;
		this.mPressure=mPressure;
		this.mHumidity=mHumidity;
		dataChange();
	}

	public void dataChange(){
		mCurrentConditions.update(getTemperature(), getPressure(), getHumidity());
	}

	public float getTemperature(){
		return mTemperatrue;
	}

	public float getPressure(){
		return mPressure;
	}

	public float getHumidity(){
		return mHumidity;
	}
}


/**
 * 测试类
 */
public class InternetWeather {

	public static void main(String[] args) {
		//公告板
		CurrentConditions mCurrentConditions;
		//天气信息产生器
		WeatherData mWeatherData;
		
		mCurrentConditions = new CurrentConditions();
		mWeatherData =new WeatherData(mCurrentConditions);
		
		mWeatherData.setData(30, 150, 40);
	}

}

1.3 需求变动及带来问题

如果产生新的公告板或者其它要显示天气信息设备出现:那么则要修改天气变动类(气象站)(WeatherData中的dataChange方法),将新的公告板增加到dataChange中,如果公告板不停增加则将多次修改,且在修改期间还会影响原有公告板的使用.

1.4 带来问题

  • 扩展性差,其他第三方公司接入气象站获取数据的问题
  • 影响原公告栏的使用,无法在运行时动态的添加第三方

2.用设计模式改进

2.1 分析

观察者模式就像定报纸业务

  • 报纸奶站,Subject(被订阅)----->登记注册、移除和通知
  • 用户,Observer(观察)----->Observer:接收输入

2.2 重新设计

02观察者模式-2.png

2.3 代码

  • (被)订阅和观察接口
/**
 * (被)订阅者接口
 */
public interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}

/**
 * 观察者接口
 */
public interface Observer {
	public void update(float mTemperatrue, float mPressure, float mHumidity);
}

  • (被)订阅和观察实现运用

import java.util.ArrayList;

/**
 * (被)订阅者实现
 */
public class WeatherDataSt implements Subject {
	
	private float mTemperatrue;
	private float mPressure;
	private float mHumidity;
	private ArrayList<Observer> mObservers;
	public WeatherDataSt(){
		mObservers=new ArrayList<Observer>();
	}

	@Override
	public void registerObserver(Observer o) {
		mObservers.add(o);
	}

	@Override
	public void removeObserver(Observer o) {
		if(mObservers.contains(o))
		{mObservers.remove(o);}
	}

	public void setData(float mTemperatrue,float mPressure,float mHumidity){
		this.mTemperatrue=mTemperatrue;
		this.mPressure=mPressure;
		this.mHumidity=mHumidity;
		dataChange();
	}

	public void dataChange(){
		notifyObservers();
	}

	@Override
	public void notifyObservers() {
		for(int i=0,len=mObservers.size();i<len;i++)
		{
			mObservers.get(i).update(getTemperature(), getPressure(), getHumidity());
		}
	}


	public float getTemperature(){
		return mTemperatrue;

	}

	public float getPressure(){
		return mPressure;

	}

	public float getHumidity(){
		return mHumidity;

	}

}

/**
 *  观察者实现1-公告板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) {
		this.mHumidity = mHumidity;
		this.mPressure = mPressure;
		this.mTemperatrue = mTemperatrue;
		display();
	}

	public void display() {
		System.out.println("CurrentConditions-->***Today mTemperatrue:" + mTemperatrue + "***");
		System.out.println("CurrentConditions-->***Today mPressure:" + mPressure + "***");
		System.out.println("CurrentConditions-->***Today mHumidity:" + mHumidity + "***");
	}
}



/**
 * 观察者实现2-公告板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) {
		this.mTemperatrue=mTemperatrue;
		this.mPressure=mPressure;
		this.mHumidity=mHumidity;
		
		display();
	}

	public void display(){
		System.out.println("ForcastConditions---->**明天温度:"+(mTemperatrue+Math.random())+"**");
		System.out.println("ForcastConditions---->**明天气压:"+(mPressure+10*Math.random())+"**");
		System.out.println("ForcastConditions---->**明天湿度:"+(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);
	}

}


3 Java内置观察者

3.1 核心要点

  • class Observable (被)订阅者继承该类
    • 当发生改变的时候调用Observable.setChanged()和notifyObservers(Object arg);
   /**
	 * 调用Observable的改变方法和通知观察者的方法
	 */
	public void dataChange(){
		this.setChanged();
		this.notifyObservers(Object);
	}
  • interface Observer 观察者实现该接口
    • 实现update方法
@Override
	public void update(Observable arg0, Object arg1) {
	    MyObject o = (MyObject)arg1;
	}
	

3.2 代码示例

  • (被)订阅者 WeatherData

import java.util.Observable;

/**
* 观察者实现
*/
public class WeatherData extends Observable{
   private float mTemperatrue;
   private float mPressure;
   private float mHumidity;
   public void setData(float mTemperatrue,float mPressure,float mHumidity){
   	this.mTemperatrue=mTemperatrue;
   	this.mPressure=mPressure;
   	this.mHumidity=mHumidity;
   	dataChange();
   }


   /**
    * 调用Observable的改变方法和通知观察者的方法
    */
   public void dataChange(){
   	this.setChanged();
   	this.notifyObservers(new Data(getTemperature(),getPressure(),getHumidity()));
   }

   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 float getTemperature(){
   	return mTemperatrue;
   }

   public float getPressure(){
   	return mPressure;
   }

   public float getHumidity(){
   	return mHumidity;
   }
}
  • CurrentConditions 观察者1
import java.util.Observable;
import java.util.Observer;
/**
 * 观察者1
 */
public class CurrentConditions implements Observer {
	
	private float mTemperatrue;
	private float mPressure;
	private float mHumidity;
	@Override
	public void update(Observable arg0, Object arg1) {
		this.mTemperatrue=((WeatherData.Data)(arg1)).mTemperatrue;
		this.mPressure=((WeatherData.Data)(arg1)).mPressure;
		this.mHumidity=((WeatherData.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+"***");
	}
}
  • ForcastConditions 观察者2
import java.util.Observable;
import java.util.Observer;

/**
 * 观察者2
 */
public class ForcastConditions implements Observer  {
	
	private float mTemperatrue;
	private float mPressure;
	private float mHumidity;

	@Override
	public void update(Observable arg0, Object arg1) {
		this.mTemperatrue=((WeatherData.Data)(arg1)).mTemperatrue;
		this.mPressure=((WeatherData.Data)(arg1)).mPressure;
		this.mHumidity=((WeatherData.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)+"***");
	}
}

  • InternetWeather 测试类
/**
 * 测试类
 */
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);
		
	}
}

4.设计模式总结

4.1 定义

观察者模式: 对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对 象为Observer,Subject通知Observer变化

观察者模式: 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的依赖者都会收到通知并更新

4.2 注意

  • 松耦合、高内聚、隔离影响的意义

5. 设计模式使用场景及注意

6.参考文章

内容总计于HeadFirst设计模式及相关视频
读headFirst设计模式 - 观察者模式