一、观察者模式的定义:
观察者模式:软件设计模式的一种,在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
PS:观察者模式与发布订阅模式的区别,本质上的区别是调度的不同。
在观察者模式中,目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。

在发布订阅模式中,订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。

两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。

二、观察者模式结构图
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。该模式必须包含两个角色:观察者和被观察对象。观察者和被观察者之间存在“观察”的逻辑关联,当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应。
观察者模式通常使用一种“注册——通知——撤销注册”的形式,其结构如下图所示:

观察者模式包含的角色:
●  抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

  ●  具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

  ●  抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  ●  具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

三、示例代码
参考GitHub中的事例https://github.com/234Spring18/observerUML/tree/master/observer/weather ,给出观察者模式类图如下所示:

抽象主题(Subject)角色接口:

public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}

具体主题(ConcreteSubject)角色:天气数据类

public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;

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 void measurementsChanged() {
	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;
}

}

抽象观察者角色接口:观察者接口和显示接口

public interface Observer {
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
public void display();
}

具体观察者角色:时间显示类

public class TimDisplay implements Observer, DisplayElement {
private float humidity;
private float pressure;
private float temp;
private WeatherData weatherData;

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

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

public void display() {
	if(humidity >= 100){
			if(temp < 32){
  				System.out.println(“It’s going to snow”);
			}
			else if(temp <= 32){
  				System.out.println(“it’s going to rain”);
			}
		
	}
	else
		System.out.println(“no precipitation today”);

}
数据显示类
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);
}

public void update(float temp, float humidity, float pressure) {
	tempSum += temp;
	numReadings++;

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

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

	display();
}

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

}

四、特性
1、优点
第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知。
2、缺点
第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
3、观察者模式应用场景
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

posted on 2018-10-24 09:12  蒋成满  阅读(304)  评论(0编辑  收藏  举报