`
bcyy
  • 浏览: 1829364 次
文章分类
社区版块
存档分类
最新评论

设计模式学习--观察者模式(Observer Pattern)

 
阅读更多

设计模式学习--观察者模式(Oberser Pattern)

2013年5月18日 天气:热!
下午15:28 设计模式学习中
学习者:小巫



什么是观察者模式?

定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。


怎么设计一个观察者模式的程序?

确定两个对象:1. 主题
2.观察者

确定这两个对象之间的关系:

主题对象管理某些数据,一旦数据发生改变,会主动向观察者进行通知,然而观察者不必向主题进行索取。
主题并不知道具体的观察者是谁,这是它们之间的关系。

以上涉及到的设计原则:

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


具体实例:气象站的实现

1. 定义一个主题接口Subject
package observerPattern;
/**
 * 主题接口
 * @author wwj
 *
 */
public interface Subject {
	public void registerObserver(Observer o);	//这两个方法都需要一个观察者作为变量,该观察者是用那个来注册和删除的
	public void removeObserver(Observer o);
	public void notifyObserver();				//当主题状态发生改变时,这个方法会被调用,以通知所有的观察者
}

2. 定义一个观察者接口Observer
package observerPattern;
/**
 * 观察者接口
 * @author wwj
 *
 */
public interface Observer {
	public void update(float temp, float humidity, float pressure);
}

3. 定义一般气象布告板接口DisplayElement
package observerPattern;

/**
 * 公告板接口
 * @author wwj
 *
 */
public interface DisplayElement {
	public void display();
}	


4. 定义主题类:WeatherData实现接口
package observerPattern;

import java.util.ArrayList;

/**
 * WeatherData实现了Subject接口
 * @author wwj
 *
 */
public class WeatherData implements Subject {
	private ArrayList observers;	//用于记录观察者
	private float temperature;		//温度
	private float humidity;			//湿度
	private float pressure;			//压力
	

	public WeatherData() {
		observers = new ArrayList();
	}

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

	@Override
	public void removeObserver(Observer o) {
		int i = observers.indexOf(o);
		if(i >= 0) {
			observers.remove(i);
		}
	}

	@Override
	public void notifyObserver() {
		for(int i = 0; i < observers.size(); i++) {
			Observer observer = (Observer)observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
	}
	
	public void measurementsChanged() {
		notifyObserver();
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}

}

5. 定义四个布告板类实现观察者接口和布告板接口
package observerPattern;
/**
 * 观察者类实现观察者接口和显示板接口
 * @author wwj
 *
 */
public class CurrentConditionDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private Subject weathderData;
	
	
	public CurrentConditionDisplay(Subject weathderData) {
		this.weathderData = weathderData;
		weathderData.registerObserver(this);		//注册
	}

	@Override
	public void display() {
		System.out.println("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");
	}

	@Override
	public void update(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		display();
	}

}

package observerPattern;
/**
 * 天气统计布告板
 * @author wwj
 *
 */
public class StatisticsDisplay implements Observer, DisplayElement {
	private float maxTemp = 0.0f;;	//最大温度
	private float minTemp = 200;	//最小温度
	private float tempSum = 0.0f;	//统计温度和
	private int numReadings;		//统计温度次数
	private WeatherData weatherData;
	
	
	public StatisticsDisplay(WeatherData weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	@Override
	public void display() {
		System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);
	}

	@Override
	public void update(float temp, float humidity, float pressure) {
		tempSum += temp;
		numReadings++;
		
		if(temp > maxTemp) {
			maxTemp = temp;
		}
		if(temp < minTemp) {
			minTemp = temp;
		}
		
		display();
	}

}

package observerPattern;
/**
 * 天气预报布告板
 * @author wwj
 *
 */
public class ForecastDisplay implements Observer, DisplayElement {
	private float currentPressure = 29.92f;	//当前气压
	private float lastPressure;				//以往气压
	private WeatherData weatherData;
	
	public ForecastDisplay(WeatherData weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}
	
	@Override
	public void display() {
		System.out.println("Forcast:");
		if(currentPressure > lastPressure) {
			System.out.println("Improving weather on the way!");
		} else if(currentPressure == lastPressure) {
			System.out.println("more of the same");
		} else if(currentPressure < lastPressure) {
			System.out.println("Watch out for cooler, rainy weather");
		}
	}

	@Override
	public void update(float temp, float humidity, float pressure) {
		lastPressure = currentPressure;
		currentPressure = pressure;
		display();
	}

}

package observerPattern;

/**
 * 酷热指数布告板
 * 
 * @author wwj
 * 注:那个计算酷热指数的公式不必深究
 */
public class HeatIndexDisplay implements Observer, DisplayElement {
	float heatIndex = 0.0f;
	private WeatherData weatherData;

	public HeatIndexDisplay(WeatherData weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	public void update(float t, float rh, float pressure) {
		heatIndex = computeHeatIndex(t, rh);
		display();
	}
	
	private float computeHeatIndex(float t, float rh) {
		float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) 
			+ (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) 
			+ (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +
			(0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * 
			(rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + 
			(0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +
			0.000000000843296 * (t * t * rh * rh * rh)) -
			(0.0000000000481975 * (t * t * t * rh * rh * rh)));
		return index;
	}

	public void display() {
		System.out.println("Heat index is " + heatIndex);
	}
}

6. 来吧,开始测试
package observerPattern;

/**
 * 测试类
 * @author wwj
 *
 */
public class WeatherStation {
	public static void main(String[] args) {
		//建立一个WeatherData对象
		WeatherData weatherData = new WeatherData();
		
		//第一个布告板
		CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(
				weatherData);
		StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
		HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);

		// 模拟新的气象数据
		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);
	}
}

7. 测试结果:
Current coditions: 80.0F degress and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forcast:
Improving weather on the way!
Heat index is 82.95535
Current coditions: 82.0F degress and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forcast:
Watch out for cooler, rainy weather
Heat index is 86.90124
Current coditions: 78.0F degress and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forcast:
more of the same
Heat index is 83.64967



以上的观察者模式实现是通过主题以“推”的方式通知观察者们,观察者可以在一次通知中一口气得到所有东西。

因为观察者与主题发生了争吵,观察者有自己的想法,希望能“拉”走主题的状态,然而Java内置的Observer模式就支持这样,下面来看看吧。

1. 继承Observable类的WeatherData(不再需要自定义接口了,但这样真的好吗?)
package weatherObservable;

import java.util.Observable;
/**
 * 使用Java内置的观察者模式
 * @author wwj
 *
 */
public class WeatherData extends Observable {
	private float temperature;
	private float humidity;
	private float pressure;
	
	/**
	 * 我们的构造器不再需要为了记住观察者们而建立数据结构了
	 */
	public WeatherData(){}
	
	public void measurementsChanged() {
		setChanged();		//Observable类方法
		notifyObservers();
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}
	
}

2. 4个布告板中的代码稍微发生了点变化
package weatherObservable;

import java.util.Observable;
import java.util.Observer;


/**
 * 实现Java内置的观察者接口,布告板不变
 * @author wwj
 *
 */
public class CurrentConditionDisplay implements Observer, DisplayElement{
	Observable observable;
	private float temperature;
	private float humidity;
	
	public CurrentConditionDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);		//登记为观察者
	}
	
	@Override
	public void display() {
		System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
	}

	
	/**
	 * 在这个方法当中,先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和温度测量值,最后调用display();
	 */
	@Override
	public void update(Observable obs, Object arg) {
		if(obs instanceof WeatherData) {
			WeatherData weatherData = (WeatherData) obs;
			this.temperature = weatherData.getTemperature();
			this.humidity = weatherData.getHumidity();
			display();
		}
	}

}

package weatherObservable;

import java.util.Observable;
import java.util.Observer;

/**
 * 天气预报布告板
 * @author wwj
 *
 */
public class ForecastDisplay implements Observer, DisplayElement {
	private Observable observable;
	private float currentPressure = 29.92f;	//当前气压
	private float lastPressure;				//以往气压
	
	public ForecastDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}
	
	@Override
	public void display() {
		System.out.println("Forcast:");
		if(currentPressure > lastPressure) {
			System.out.println("Improving weather on the way!");
		} else if(currentPressure == lastPressure) {
			System.out.println("more of the same");
		} else if(currentPressure < lastPressure) {
			System.out.println("Watch out for cooler, rainy weather");
		}
	}


	@Override
	public void update(Observable o, Object arg) {
		if (o instanceof WeatherData) {
			WeatherData weatherData = (WeatherData)observable;
			lastPressure = currentPressure;
			currentPressure = weatherData.getPressure();
			display();
		}
	}

}

package weatherObservable;

import java.util.Observable;
import java.util.Observer;

/**
 * 天气统计布告板
 * @author wwj
 *
 */
public class StatisticsDisplay implements Observer, DisplayElement {
	private float maxTemp = 0.0f;;	//最大温度
	private float minTemp = 200;	//最小温度
	private float tempSum = 0.0f;	//统计温度和
	private int numReadings;		//统计温度次数
	private Observable observable;
	
	public StatisticsDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}

	@Override
	public void display() {
		System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);
	}

	@Override
	public void update(Observable obs, Object arg) {
		if(obs instanceof WeatherData) {
			WeatherData weatherData = (WeatherData)obs;
			float temp = weatherData.getTemperature();
			tempSum += temp;
			numReadings++;

			if (temp > maxTemp) {
				maxTemp = temp;
			}
 
			if (temp < minTemp) {
				minTemp = temp;
			}

			display();
		}
	}


}

package weatherObservable;

import java.util.Observable;
import java.util.Observer;

/**
 * 酷热指数布告板
 * 
 * @author wwj
 * 注:那个计算酷热指数的公式不必深究
 */
public class HeatIndexDisplay implements Observer, DisplayElement {
	float heatIndex = 0.0f;
	private WeatherData weatherData;
	private Observable observable;

	public HeatIndexDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}

	private float computeHeatIndex(float t, float rh) {
		float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) 
			+ (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) 
			+ (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +
			(0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * 
			(rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + 
			(0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +
			0.000000000843296 * (t * t * rh * rh * rh)) -
			(0.0000000000481975 * (t * t * t * rh * rh * rh)));
		return index;
	}

	public void display() {
		System.out.println("Heat index is " + heatIndex);
	}

	@Override
	public void update(Observable obs, Object arg) {
		if(obs instanceof WeatherData) {
			WeatherData weatherData = (WeatherData)observable;
			float t = weatherData.getTemperature();
			float rh = weatherData.getHumidity();
			heatIndex = computeHeatIndex(t, rh);
		}
		display();
	}
}

3. 测试类不变
package weatherObservable;

/**
 * 测试类
 * @author wwj
 *
 */
public class WeatherStation {
	public static void main(String[] args) {
		//建立一个WeatherData对象
		WeatherData weatherData = new WeatherData();
		
		//第一个布告板
		CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(
				weatherData);
		StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
		HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);

		// 模拟新的气象数据
		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);
	}
}

4. 但测试结果发生了变化:观察者被通知的次序发生了变化
Heat index is 82.95535
Forcast:
Improving weather on the way!
Avg/Max/Min temperature = 80.0/80.0/80.0
Current conditions: 80.0F degrees and 65.0% humidity
Heat index is 86.90124
Forcast:
Watch out for cooler, rainy weather
Avg/Max/Min temperature = 81.0/82.0/80.0
Current conditions: 82.0F degrees and 70.0% humidity
Heat index is 83.64967
Forcast:
more of the same
Avg/Max/Min temperature = 80.0/82.0/78.0
Current conditions: 78.0F degrees and 90.0% humidity

以上的实现被认为不是那么“正确”的,为什么呢?
有以下原因:
1. Observable是一个“类”,而不是一个接口,也没有实现一个接口,限制了它的使用和复用。
2. Observable将关键的方法保护起来了,违反了 “多用组合,少用继承”的设计原则。



当然在JDK中不只有这个地方用到了观察者模式,比如以下几个地方也用到了:
1. Swing
2. JavaBeans
3. RMI



好啦,第二个设计模式:观察者模式被小巫收入囊中,这实在太有趣了。
下一个模式将会是:装饰者模式。


分享到:
评论

相关推荐

    详解Observer Pattern(观察者模式)在Java中的使用原理

    我们说学习Java应该从Swing开始,那么学习Swing最重要的思想就是对于观察者模式的理解(Observer Pattern)。因为,该设计模式在Java Swing框架中贯穿了始终。对于C#的委托、代理概念所使用的Callback(回调模式--...

    设计模式之观察者模式(Observer Pattern)

    定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。

    design-pattern-java.pdf

    撤销功能的实现——备忘录模式(三) 撤销功能的实现——备忘录模式(四) 撤销功能的实现——备忘录模式(五) 观察者模式-Observer Pattern 对象间的联动——观察者模式(一) 对象间的联动——观察者模式(二) ...

    observer-pattern.rar

    文章链接:https://blog.csdn.net/qq_44901285/article/details/116092808?spm=1001.2014.3001.5501 Spring事件机制(Event)-- 基于观察者模式实现多任务同时处理(源码)

    ObserverPattern

    设计模式之观察者模式ObserverPattern

    C#设计模式(17)——观察者模式(Observer Pattern).pdf

    从生活中的例子可以看出,只要对订阅号... 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己的行为。

    观察者模式(Observer)原理图

    观察者模式(Observer Pattern)是一种对象行为型设计模式,它定义了对象之间的一对多依赖关系。 当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式通常用于实现分布式事件处理系统...

    design-pattern-observer:GOF观察者行为设计模式示例

    观察者模式 行为模式 在对象之间定义一对多的依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖关系。 在此示例中,我们要监视节点上的子创建/删除事件(可用于监视文件夹) 源代码源于以下UML图:

    dessign-pattern-observer:观察者设计模式示例程序。 日本公司的编码测试

    设计模式观察者 观察者设计模式示例程序。 日本公司的编码测试。 它是什么? 它是基于 Uzabase 公司 ( ) 分配的应用程序 可以在以下链接中找到作业(日文版): 我做的应用程序是新版本的uzabase_assignment.rar,...

    用Java实现23种设计模式

    观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) 4. J2EE 模式 ...

    Java24种设计模式,Java24种设计模式,24种设计模式,学会了这24种设计模式,可以打遍天下无敌手,设计模式非常重要

    1、策略模式STRATEGY PATTERN ...16、观察者模式OBSERVER PATTERN 17、责任链模式 18、访问者模式VISITOR PATTERN 19、状态模式 20、原型模式 21、中介者模式 22、解释器模式 23、亨元模式 24、备忘录模式

    C#版 24种设计模式

    工厂方法模式(Factory Method Pattern) 观察者模式(Observer Pattern) 建造者模式(Builder Pattern) 解释器模式(Interpreter Pattern) 命令模式(Command Pattern) 模板方法模式(Template Method Pattern) 桥接模式...

    C++设计模式(Design Pattern)范例源代码

    23种设计模式(Design Pattern)的C++实现范例,包括下面列出的各种模式,代码包含较详细注释。...观察者模式(Observer) 状态模式(State) 策略模式(Strategy) 模板方法模式(Template Method) 访问者模式(Visitor)

    C#设计模式.PDF

    设计模式(19)-Observer Pattern 178 一、 观察者(Observer)模式 178 二、 观察者模式的结构 179 三、 观察者模式的示意性源代码 180 四、 C#中的Delegate与Event 183 五、 一个实际应用观察者模式的例子 187 六...

    33种JAVA设计模式DEMO

    观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) 4 J2EE 模式 这些设计...

    设计模式PPT

    创建型模式用来处理对象的创建... 观察者模式(Observer Pattern)  状态模式(State Pattern)  策略模式(Strategy Pattern)  模板方法模式(Template Method Pattern)  访问者模式(Visitor Pattern)

    C#设计模式大全

    设计模式(19)-Observer Pattern 一、 观察者(Observer)模式 二、 观察者模式的结构 三、 观察者模式的示意性源代码 四、 C#中的Delegate与Event 五、 一个实际应用观察者模式的例子 六、 观察者模式的优...

    java观察者模式.doc

    在设计一组依赖的对象与它们所依赖的对象之间一致(同步)的交流模型时,观察者模式(Observer Pattern)很有用。它可以使依赖对象的状态与它们所依赖的对象的状态保持同步。这组依赖的对象指的是观察者(Observer)...

    C#设计模式_设计模式_C#_

    观察者模式(Observer Pattern) 17. 解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式...

    JavaScript编程设计模式之观察者模式(Observer Pattern)实例详解

    主要介绍了JavaScript编程设计模式之观察者模式(Observer Pattern),简单说明了观察者模式的概念、原理并结合实例形式详细给出了观察者模式的相关实现与使用技巧,需要的朋友可以参考下

Global site tag (gtag.js) - Google Analytics