《Head First设计模式》读书笔记 -- (第二章)观察者模式

本章要点:

1.观察者模式定义了对象之间一对多的关系。

2.主题(也就是可观察者)用一个共同的接口来更新观察者。

3.观察者和可观察者之间用松耦合方式结合(loosecoupling),可观察者不知道观察者的细节,只知道观察者实现了观察者接口。

4.使用此模式时,你可从被观察者处推(push)或拉(pull)数据(然而,推得方式被认为更“正确”)。

5.有多个观察者时,不可以依赖特定的通知次序。

6.Java有多重观察者模式的实现,包括了通用的java.util.Observable。

7.要注意java.util.Observable实现上所带来的一些问题。

8.如果有必要的话,可以实现自己的Observable,这并不难,不要害怕。

9.Swing大量使用观察者模式,许多GUI框架也是如此。

10.此模式也被应用在许多地方,例如:JavaBeans、RMI。


----------------------------------------分割线 ----------------------------------------

观察者模式类图:


下面是实例:



现在要做一个气象监测系统,客户提供了一个WeatherData对象(追踪来自气象站的数据,并更新布告板)。我们的工作就是建立一个应用,利用WeatherData对象取得数据,并更新N个布告板。

WeatherData类具有getter方法,可以取得三个测量值:温度、湿度和气压。

● 当新的测量数据备妥时,measurementsChanged()方法就会被调用(我们不在乎此方法是如何被调用的,我们只在乎它 被调用了)。

● 我们需要实现三个使用天气数据的布告板:“目前状况”、“气象统计”和“天气预报”。一旦WeatherData有新的测量,这些布告必须马上更新。

● 此系统必须可扩展,让其他开发人员建立定制的布告板,用户可以随心所欲地添加或删除任何布告板。


此系统可以用观察者模式实现,下面先从建立接口开始吧:

public interface Subject {
	//这两个方法都需要一个观察者作为变量,该观察者是用来注册或被删除的。
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	//当主题状态被改变时,这个方法会被调用,以通知所有的观察者。
	public void notifyObservers();
}

public interface Observer {
	//当气象观测值改变时,主题会把状态值当做方法的参数,传给观察者。Weather对象中包含温度、湿度与气压。
	public void update(Weather weather);
}

public interface DisplayElement {
	//DisplayElement接口只包含了一个方法,当布告板需要显示时,调用此方法。
	public void display();
}
实现WeatherData类(主题)

public class WeatherData implements Subject {
	//加上一个list来记录观察者,此list在构造器中建立的。
	private List<Observer> observers;
	private Weather weather;
	
	public WeatherData() {
		observers = new ArrayList<Observer>();
	}
	//当注册观察者时,我们只要把它加到list即可。
	public void registerObserver(Observer o) {
		observers.add(o);
	}
	//同样的,当观察者想取消注册,我们把它从list中删除即可。
	public void removeObserver(Observer o) {
		int i = observers.indexOf(o);
		if(i>0)
			observers.remove(i);
	}
	//在这里,我们把状态告诉每一个观察者。因为观察者都实现了update(),所以我们知道如何通知他们。
	public void notifyObservers() {
		for (Observer observer : observers) {
			observer.update(weather);
		}
	}
	//当从气象站得到更新观测值时,我们通知观察者。
	public void measurementsChanged() {
		notifyObservers();
	}
	
	public void setMeasurements(Weather w) {
		this.weather = w;
		measurementsChanged();
	}

}
建立布告板(观察者)
public class CurrentConditionsDisplay implements Observer, DisplayElement {
	private Weather weather;
	private Subject weatherData;
	//构造器需要weatherData对象(主题)作为注册之用。
	public CurrentConditionsDisplay(Subject weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}
	public void update(Weather weather) {
		//当update()被调用时,我们把温度和湿度保存起来,然后调用display()。
		this.weather = weather;
		display();
	}
	public void display() {
		//display()方法就只是把最近的温度和湿度显示出来。
		System.out.println("Current conditions:"+weather.getTemp()
				+"F degrees and "+weather.getHumidity()+"% humidity");
	}
}
编写测试类

public class App {
	public static void main(String[] args) {
		//首先,建立一个WeatherData对象(主题)
		WeatherData weatherData = new WeatherData();
		//建立布告板,并把WeatherData对象传给它
		CurrentConditionsDisplay conditionsDisplay = 
				new CurrentConditionsDisplay(weatherData);
		//模拟数据
		Weather weather1 = new Weather(80, 65, 30.4f);
		Weather weather2 = new Weather(82, 70, 29.2f);
		//新的气象测量
		weatherData.setMeasurements(weather1);
		weatherData.setMeasurements(weather2);
	}
}

输出结果:Current conditions:80.0F degrees and 65.0% humidity
  Current conditions:82.0F degrees and 70.0% humidity

以上是我们自己建立的观察者模式,其实Java API为观察者模式提供了内置的支持。java.util包内包含最基本的 Observer接口与Observable类,这和我们的Subject接口与Observer接口很相似。Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。你甚至可以使用推(push)或拉(pull)的方式传送数据。


首先,把WeatherData改成使用java.util.Observable

/**
 * 我们现在正在继承Observable
 * 我们不再需要追踪观察者了,也不需要管理注册与删除(让超类代劳即可)
 * 所以我们把注册、添加、通知的相关代码删除
 */
public class WeatherData extends Observable {
	private Weather weather;
	//我们的构造器不再需要为了记住观察者们而建立数据结构了
	public WeatherData() {
	}
	//当从气象站得到更新观测值时,我们通知观察者
	public void measurementsChanged() {
		//在调用notifyObservers()之前,要先调用setChanged()来指示状态已经改变
		setChanged();
		//我们没有调用notifyObservers()传送数据对象,这表示我们采用"拉"
		notifyObservers();
	}
	public Weather getWeather() {
		return weather;
	}
	public void setMeasurements(Weather w) {
		this.weather = w;
		measurementsChanged();
	}
现在,让我们重做 CurrentConditionsDisplay

//我们现在正在实现java.util.Observer接口
public class CurrentConditionsDisplay implements Observer, DisplayElement {
	private Weather weather;
	private Observable observable;
	//现在构造器需要一个Observable当参数,并将CurrentConditionsDisplay对象登记成为观察者
	public CurrentConditionsDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}
	//改变update()方法,增加Observable和数据对象作为参数
	public void update(Observable obs, Object arg) {
		//在update()中,先确定可观察者属于WeatherData类型
		if(obs instanceof WeatherData){
			WeatherData weatherData = (WeatherData) obs;
			this.weather = weatherData.getWeather();
			display();
		}
	}
	public void display() {
		System.out.println("Current conditions:"+weather.getTemp()
				+"F degrees and "+weather.getHumidity()+"% humidity");
	}
}
细心的同学可能已经发现了一个问题,java.util.Observable是一个“类”而不是一个“接口”,更糟的是,它甚至没有实现一个接口。如果某类想同时具有 Observable类另一个超类的行为,就会陷入两难,毕竟Java不支持多继承。如果你能够扩展java.util.Observable,那么Observable可能可以符合你的需求。否则,你可能需要像我们开头的做法那样自己实现这一整套观察者模式,尽管它并不是很复杂。


现在来总结一下,一个新的设计原则:

 为了交互对象之间的松耦合设计而努力。

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值