设计模式二之观察者模式(java)

   这是我看Head first设计模式书籍之后想要总结的知识点,一方面是对自己学习的东西总结和提炼加强自己的理解和记忆,另一方面是给大家简化这本书,方便大家快速了解各种设计模式。

我想提醒大家的是,设计模式只是前人总结的一些经验套路,实际上还是要在开发项目中慢慢体会,不可成为设计模式的中毒患者,强行照搬设计模式的一些规则。


我们这次要讲解的设计模式是观察者模式

我们举个栗子:

比如说我们要设计一个气象站应用,由WeatherData对象负责追踪目前的天气状况(温度、湿度、气压),我们有三个布告板,分别显示当前的情况,气象统计及简单预报。当WeatherDataObject对象获得最新的测量数据时,三种布告板必须实时更新。

WeatherData中提供了getTemperature()、getHumidity()、getPressure()、measurementsChanged()、measurementsChanged()等方法,另外一旦气象测量更新,measurementsChanged()方法会被调用。我们的任务是实现measurementsChanged(),好让它更新目前状况,气象统计,天气预报的显示布告板。


第一种思路(也是错误的思路):

public class WeatherData{

	//实例变量声明

	float temp = getTemperature();
	float humidity = getHumidity();
	float pressure = getPressure();

	currentConditionsDisplay.update(temp. humidity, pressure);
	statisticsDisplay.update(temp, humidity, pressure);
	forecastDisplay.update(temp, humidity, pressure);
	
}

这样做带来的问题: 

1.针对具体实现编程,而非针对接口。这样我们增加或删除布告板时必须修改WeatherData类中的代码

2.要将改变的地方封装起来,这样耦合性太高了。

3.无法在运行时动态地增加或删除布告板


下面就是我们的观察者模式出场了!

观察者模式: 

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


实现观察者模式的方法不只一种,但是以包含Subject和Observer接口的类设计的做法最常见。

主题接口Subject :  

    包含方法:抽象方法registerObserver() removeObserver() notifyObservers()

    作用:对象使用此接口注册为观察者,或者把自己从观察中删除 当主题状态发生更新时,通知观察者比起让许多对象控制同一份数据来,可以得到更干净的OO设计。

ConcreteSubject: 

    包含方法: 实现Subject中的方法以及一些其他方法

    作用: 多个主题的具体类

观察者接口Observer: 

    包含方法: update() 

    作用: 多个观察者可以实现这个接口,通过当Subject实现类状态改变时可以通过update()调用来接受通知和数据


这样做的好处: 

松耦合 当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计

,让主题和观察者之间松耦合,能够建立有弹性的系统,应对变化,这是因为对象之间的互相依赖降到了最低。

这里依赖就只有主题中实现Observer接口的对象列表。

主题: 只知道观察者实现了Observer接口,有个update()方法可以让我通知观察者


直接看代码部分: 

建立主题Subject、观察者Observer、DisplayElement(三个公告布实现这个接口,这里我们就实现一个公告布类)接口

public interface Subject{

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

public interface Observer{
	//当状态改变时,主题会把这些状态值当做方法的参数,传送给观察者
	public void update(float temp, float humidity, float pressure);
}

public interface DisplayElement{
	public void display();
}

WeatherData类实现主题Subject接口

public class WeatherData implements Subject{

	private ArrayList observers; //用于记录观察者
	private float temperature;
	private float pressure;
	private float humidity;

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

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

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

	public void notifyObservers(){
		for(int i = 0; i < observers.size(); i++){
			Observer observer = (Observer) observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
	}

	//当状态改变时,会自动回调这个方法
	public vooid measurementsChanged(){
		notifyObservers();
	}

	public void setMeasurements(float temperature, float humidity, float pressure){
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}

	//其他方法

}


布告板(实现了观察者Observer接口)

public class CurrentConditionsDisplay implements Observer, DisplayElement{

	private float temperature;
	private float humidity;
	private Subject weatherData;

	//构造器需要weatherData对象作为注册之用
	public CurrentConditionsDisplay(Subject weatherData){
		this.weatherData = weatherData;
		weatherData.registerObserver();
	}

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

	public void display(){
		System.out.println("Current conditions: " + temperature
			+ "F degrees and " + humidity + "% humidity");
	}
}


测试程序

public class WeatherStation{

	public static void main(String args[]){
		WeatherData weatherData = new WeatherData();

		CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

		weatherData.setMeasurements(80, 65, 30.4f);
	}
}

我们从无到有实现了观察者模式,但是Java API有内置的观察者模式

java.util包内包含基本的Observer接口(观察者接口)和Observable(可观察者类)

注意Observable是一个主题类,有addObserver()和deleteObserver()以及notifyObservers()的具体实现方法,也有一个setChanged()方法 

我们让WeatherData类继承这个Observable类,观察者CurrentConditionsDisplay实现Observer接口

我们使用java的内置API来演示一遍

WeatherData

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

public class WeatherData extends Observable{


	//private ArrayList observers;
	/*不需要记录观察者,注册,添加和通知代码,父类已经替我们做好了*/
	private float temperature;
	private float pressure;
	private float humidity;

	public WeatherData(){}

	public void measurementsChanged(){
		setChanged(); //这个方法setChanged()可以让changed变量为true,可以通知观察者,否则就不能通知。因为我们不能状态变化一点就通知观察者,要有个限制
		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 getPressure(){
		return pressure;
	}

	public float getHumidity(){
		return humidity();
	}
}


CurrentConditionsDisplay观察者类

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

public class CurrentConditionsDisplay implements Observer, DisplayElement{

	Observable observable;
	private float temperature;
	private float humidity;

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

	public void update(Observable obs, Object arg){
		if(obs instanceof WeatherData){
			WeatherData weatherData = (WeatherData) obs;
			this.temperature = weatherData.getTemperature();
			this.humidity = weatherData.getHumidity();
			display();
		}
	}

	public void display(){
		System.out.println("Current conditions: " + temperature
			+ "F degrees and " + humidity + "% humidity");
	}
}


java.util.Observable的缺点: 

Observable不是接口,而是一个类。这样限制了它的使用和复用。如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟java不支持多重继承。这限制了Observable的复用潜力。


相信很多人都写过小游戏吧或者GUI程序,如Android开发中控件、View、ViewGroup注册监听器还有Java Swing库中注册监听事件,都使用了观察者模式。

例子:

public class SwingObserverExample{

	JFrame frame;

	public static void main(String rags[]){
		SwingObserverExample example = new SwingObserverExample();
		example.go();
	}

	public void go(){
		frame = new JFrame();

		JButton button = new JButton("Should I do it?");
		button.addActionListener(new AngelListener());
		button.addActionListener(new DevilListener());
		frame.getContentPane().add(BorderLayout.CENTER, button);
		//....省略
	}

	class AngelListener implements ActionListener{
		public void actionPerformed(ActionEvent event){
			System.out.println("Don't do it, you might regred it!");
		}
	}

	class DevilListener implements ActionListener{
		public void actionPerformed(ActionEvent event){
			System.out.println("Come on, do it!");
		}
	}

}

我们定义的内部类AngulListener和DevilListener都是观察者,按钮就是主题/可观察者,按钮状态改变时(isChecked()),这样就会通知观察者执行观察者中的逻辑。


以后会总结观察者模式的代表人物--MVC模式

猜你喜欢

转载自blog.csdn.net/qq_32252957/article/details/80469207