设计模式:观察者模式

缘由

之前看懂了的设计模式,不总结总结,总觉得心里很难受。

我的理解

来个生活中的例子,有一天,一个朋友要坐公交来你家里玩,怕他找不到路,你们约定你到你家附近的公交站牌等他。假设你朋友坐公交都要一个多小时,那你什么时候去公交站牌等他呢?总部能一开始就去吧?那会白等一个小时的。你可以一直打电话问你朋友,过一会就打个电话问问他到哪里了,他告诉你之后你再决定是在家里等,还是去公交站牌等。过了一会你又打电话问他到哪里了。这样,一个小时内你打了几个电话来确定他的行踪。
好笨哦,是吧,我们让朋友通知我们不就好了吗?我们老去问个屁啊。朋友到了要下的站牌的前一站,给我们打个电话,我们就知道要出发了撒。对了,这就是观察者模式。
我们就是观察者,朋友就是被观察者。我们可以一直打电话问朋友,这就是观察者一直去询问被观察者,也可以被观察者到了合适的时候主动通知观察者。显然后一种说法更为合理。
关键是还有一种情况,我们不得不考虑,那就是当有很多朋友都要来看你时,难道你要每一个朋友都过一会打一个电话,然后确定去不去公交站牌接人吗?显然不是,我们只需要要求所有朋友到了合适的时候给我们打电话。这就是一个观察者可以同时观察几个被观察者,而不用总是去询问,只需要被观察者自己的状态发生转变之后,那么通知观察者即可。
其实一个被观察者也可以拥有很多观察者,比如你的朋友还想告诉他的父母,他已经安全到了你的家了,他的父母就是另一个观察者。

当然书上举的例子也很不错,就像订报纸一样,订报人告就是观察者,报社是被观察者。订报人告诉报社,我要订报。那么报纸一生产出来就会被送给订报人。这就是观察者的状态发生了改变,报社生产了报纸,报社就会通知订报人,就是把报纸送给订报人。
ok,这个思路说清楚之后来看一下观察者的定义:
  • 观察者模式定义了一系列对象之间的一对多关系。当一个对改变状态,其他依赖者都会收到。

类图





例子代码

有代码例子是气象站。天气的温度、湿度在不断的变化。气象局作为被观察者,电视上的天气预报的滚动条可以作为一个观察者(ForecastDisplay),气象局门口那个电子大展牌可以作为另一个观察者(CurrentConditionsDisplay),气象局的天气统计部门又是另一个观察者,等等等。一旦气象局知道了天气的变化,那么就会通知这些观察者。
书中给出的类图如下:

  • 其中DispalyElement接口其实和观察者模式没什么关系,只是用来作为显示的功能的。

实现代码

结构,其他的都是接口:


/**
 * 这是观察者模式中的主题接口,当主题发生变化是会通知观察者
 * 所以可以添加观察者和移除观察者,通知观察者
 * @author chouyou
 *
 */
public interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObserver();
}

/**
 * 这是观察者模式中观察者接口,
 * 但是这是针对气象台的布告牌所写的观察者,所以在其update方法中,主要是针对气象数据作一些处理
 * @author chouyou
 *
 */
public interface Observer {
	public void update(float temp, float humidity, float pressure);
}

/**
 * 这个恐怕和观察者模式没有什么关系,主要是和所举例子有关
 * 该接口可以当作是一个显示器,而所有的布告牌都会安装一个显示器,用于显示相关数据
 * @author chouyou
 *
 */
public interface DisplayElement {
	public void display();
}

import java.util.ArrayList;

import javax.xml.bind.NotIdentifiableEvent;


public class WeatherData implements Subject {
	private ArrayList<Observer> observers;
	private float temp;
	private float humidity;
	private float pressure;
	public WeatherData() {
		// TODO Auto-generated constructor stub
		observers= new ArrayList<>();
	}
	@Override
	public void registerObserver(Observer o) {
		// TODO Auto-generated method stub
		observers.add(o);

	}

	@Override
	public void removeObserver(Observer o) {
		// TODO Auto-generated method stub
		int i = observers.indexOf(o);
		if (i >= 0 ) {
			observers.remove(i);
		}
	}

	@Override
	public void notifyObserver() {
		// TODO Auto-generated method stub
		for(int i = 0; i < observers.size(); i ++){
			Observer observer = (Observer) observers.get(i);
			observer.update(temp, humidity, pressure);
		}
	}
	/**
	 * 当气象台的数据发生变化时,就调用通知观察者的方法
	 */
	public void measurementsChanged(){
		notifyObserver();
	}
	public void setMeasurements(float temp, float humidity, float pressure){
		this.temp = temp ;
		this.humidity = humidity ;
		this.pressure = pressure ;
		measurementsChanged();
	}

}

/**
 * 这是一个观察者的实现类,注意他必须持有他所感兴趣的主题的对象的引用
 * @author chouyou
 *
 */
public class CurrentConditionsDisplay implements Observer, DisplayElement {
	/**
	 * 除了要显示的数据,必须要拥有一个主题的对象,这样才将自己这个观察者注册到主题里面去
	 * 这也暗示了这个观察者,对哪个主题感兴趣。
	 */
	private Subject weatherDate;
	private float temp;
	private float humidity;
	
	public CurrentConditionsDisplay(Subject weatherDate) {
		// TODO Auto-generated constructor stub
		this.weatherDate = weatherDate;
		weatherDate.registerObserver(this);
	}
	
	@Override
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("Current conditions: "+temp + "humidity:" + humidity);


	}

	@Override
	public void update(float temp, float humidity, float pressure) {
		// TODO Auto-generated method stub
		this.temp = temp;
		this.humidity = humidity;
		display();
	}

}

/**
 * 这是我写的另一个观察者,比较简单。
 * 只是为了证明,一个主题能够拥有多个观察者
 * @author chouyou
 *
 */
public class ForecastDisplay implements Observer,DisplayElement{

	
	private float temp;
	private WeatherData weatherData;
	
	public ForecastDisplay(WeatherData weatherData) {
		// TODO Auto-generated constructor stub
		this.weatherData = weatherData ;
		weatherData.registerObserver(this);
	}
	@Override
	public void update(float temp, float humidity, float pressure) {
		// TODO Auto-generated method stub
		this.temp = temp;
		display();
	}

	@Override
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("天气预报,今天的天气是:"+temp);
	}

}

import test.CurrentConditionsDisplay2;
import test.WeatherData2;

/**
 * 测试用的主类
 * @author chouyou
 *
 */
public class testMain {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		WeatherData weatherData = new WeatherData();
		
		//实例化观察者们
		CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
		
		//下面我们改变了天气的数据,那么上面注册的观察们由于用于注册了监听,所以发出回应。
		weatherData.setMeasurements(80, 64, 30.4f);
		weatherData.setMeasurements(70, 63, 20.4f);
		weatherData.setMeasurements(76, 34, 32.4f);

	}

}

运行结果:

Current conditions: 80.0humidity:64.0
天气预报,今天的天气是:80.0
Current conditions: 70.0humidity:63.0
天气预报,今天的天气是:70.0
Current conditions: 76.0humidity:34.0
天气预报,今天的天气是:76.0

利用java内置的观察者模式的接口

在java.util包中
  1. 有一个类:Observable,就是被观察者。
  2. Observer接口就是观察者。
可以利用这两个完成观察者模式的功能,然而并不推荐,这是因为Observable是一个类,我们必须要继承,如果我们还想继承另一个类的话,那么就会很难办。而且我们本来就应该“多用组合,少用继承”

但是作为使用,我还是写了点代码来玩玩:

import java.util.Observable;
import java.util.Observer;//不要引错包了


public class CurrentConditionsDisplay2 implements Observer, DisplayElement {
	Observable observable;//同样,我们需要一个可被观察者的对象
	private float temp;
	private float humidity;
	public CurrentConditionsDisplay2(Observable observable ) {
		// TODO Auto-generated constructor stub
		this.observable = observable;
		observable.addObserver(this);
	}
	@Override
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("内置观察者:Current conditions: "+temp + " humidity:" + humidity);
	}
	@Override
	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub
		if(o instanceof WeatherData2){
			WeatherData2 weatherData2 = (WeatherData2) o;
			this.temp = weatherData2.getTemp();
			this.humidity = weatherData2.getHumidity();
			display();
		}
	}



}



import java.util.Observable;

public class WeatherData2 extends Observable {
	private float temp;
	private float humidity;
	private float pressure;
	
	//get方法是观察者需要调用的这个方法来获得WeatherData2的对象
	public float getTemp() {
		return temp;
	}
	public float getHumidity() {
		return humidity;
	}
	public float getPressure() {
		return pressure;
	}

	/**
	 * 当气象台的数据发生变化时,就调用通知观察者的方法
	 */
	public void measurementsChanged(){
		setChanged();//表示状态已经改变了
		notifyObservers();//通知所有的观察者
	}
	public void setMeasurements(float temp, float humidity, float pressure){
		this.temp = temp ;
		this.humidity = humidity ;
		this.pressure = pressure ;
		measurementsChanged();
	}
}

/**
 * 测试用的主类
 * @author chouyou
 *
 */
public class testMain {
	public static void main(String[] args) {
		
		WeatherData2 weatherData2 = new WeatherData2();
		CurrentConditionsDisplay2 currentDisplay2 = new CurrentConditionsDisplay2(weatherData2);
		weatherData2.setMeasurements(80, 64, 30.4f);
		weatherData2.setMeasurements(70, 63, 20.4f);
		weatherData2.setMeasurements(76, 34, 32.4f);
	}

}

运行结果:

内置观察者:Current conditions: 80.0 humidity:64.0
内置观察者:Current conditions: 70.0 humidity:63.0
内置观察者:Current conditions: 76.0 humidity:34.0


java中使用到到观察者模式的地方

一个button可以成为一个被观察者,我们只需要很简单的java内的代码就可以完成一个观察者对这个button的注册。当这个button被点击时,就是状态发生改变后,他就会通知他的观察者们。


源代码

  • 上传网盘:观察者模式
估计以后项目中不会用到吧,但是养成好习惯。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值