观察者模式也被称为发布-订阅(Publish/Subscribe)模式,它属于行为型模式的一种。观察者模式定义了一种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听。当这个主题对象状态变化时,会通知所有观察者对象并作出相应处理逻辑。
上面的内容比较抽象,这里用一个案例加深对观察者模式的理解!
需求:
提供温度、气压和湿度的接口
测量数据更新时需时时通知给第三方 (通知)
需要设计开放型API,便于其他第三方公司也能接入气象站获取数据(订阅)
1) 定义一个第三方公司的模板类
package com.dgut.edu.cn;
/**
* 第三方公司的模板类
*/
public class CurrentCondition {
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
/**
*更新信息
* @param temperature
* @param pressure
* @param humidity
*/
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
/**
* 展示数据使用 —— 每个公司的模板不一样
*/
public void display(){
System.out.println("***** the day temperature is " + temperature + "******");
System.out.println("***** the day pressure is " + pressure + "******");
System.out.println("***** the day humidity is " + humidity + "******");
}
}
2) 定义气象公司的接口类
package com.dgut.edu.cn;
public class WeatherData {
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
public WeatherData() {
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
// 温度、湿度、气压发生改变时,触发函数
public void dataChange(){
}
}
3) 因为气象站的接口中,我们需要去调用第三方接口更新数据方法,故引入该第三方公司的实现类,并且在构造方法中进行实例化
在WeatherData中,加入以下代码:
// 别人的接口
private CurrentCondition currentCondition;
public WeatherData(CurrentCondition currentCondition) {
this.currentCondition = currentCondition;
}
4) 在气象站接口中,写一个跟新数据的方法,模拟气象站自动更新数据
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
5) 在dataChange方法中去通知第三方接口更新数据
// 温度、湿度、气压发生改变时,触发函数
public void dataChange(){
currentCondition.update(temperature,pressure,humidity);
}
6) 写测试案例如下:
public class Application {
public static void main(String[] args) {
CurrentCondition currentCondition = new CurrentCondition();
WeatherData weatherData = new WeatherData(currentCondition);
weatherData.setData(20,50,100);
}
}
7) 执行结果:
***** the day temperature is 20.0******
***** the day pressure is 50.0******
***** the day humidity is 100.0******
1)其他第三方公司接入气象站获取数据的问题。
2)无法在运行时动态的添加第三方
【解释:】在上述设计中,在Weather中引入currentCondition的成员变量,那如果有新的第三方模板接入,我们又得创建一个新第三方模板变量,手动去添加更新数据的代码。这样子就很麻烦,在扩展性方面很弱。
根据这种订阅/通知的设计思路,在Weather中提供一个注册服务、移除服务、更改服务的方法,在第三方模板中,定义一个更新数据的接口Observer,在模板类去实现这个接口。而在Weather中,不在存入具体的子类,而是存一个Observer即可,这样就可以完成在不改变WeatherData接口中,动态去添加多个订阅者。
基于以上分析
Subject:登记注册、移除和通知
Observer:接收输入
【总结】到这里就基本能理解观察者模式的UML图,提供注册、移除、更改的接口,以及nodify展示数据的接口
用观察者模式设计重新设计的方案
1、写observer接口
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
2、写Subject接口
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
}
3、第三方模板类继承该Observer接口
public class CurrentCondition implements Observer {
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
/**
*更新信息
* @param temperature
* @param pressure
* @param humidity
*/
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display(){
System.out.println("***** the day temperature is " + temperature + "******");
System.out.println("***** the day pressure is " + pressure + "******");
System.out.println("***** the day humidity is " + humidity + "******");
}
}
public class ForcastCondition implements Observer{
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
/**
*更新信息
* @param temperature
* @param pressure
* @param humidity
*/
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display(){
System.out.println("***** Tomorrow temperature is " + temperature + "******");
System.out.println("***** Tomorrow pressure is " + pressure + "******");
System.out.println("***** Tomorrow humidity is " + humidity + "******");
}
}
4、Weather类继承该Subject接口
package cn;
import java.util.ArrayList;
public class WeatherDataSt implements Subject{
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
private ArrayList observerArrayList = new ArrayList();
public WeatherDataSt() {
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
// 温度、湿度、气压发生改变时,触发函数
public void dataChange(){
notifyObserver();
}
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
@Override
public void registerObserver(Observer observer) {
observerArrayList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observerArrayList.contains(observer))
observerArrayList.remove(observer);
}
@Override
public void notifyObserver() {
for (int i = 0 ; i < observerArrayList.size(); i++){
observerArrayList.get(i).update(temperature,pressure,humidity);
}
}
}
5、写测试案例如下
package cn;
public class Application {
public static void main(String[] args) {
CurrentCondition currentCondition = new CurrentCondition();
ForcastCondition forcastCondition = new ForcastCondition();
WeatherDataSt weatherData = new WeatherDataSt(currentCondition);
weatherData.registerObserver(currentCondition);
weatherData.registerObserver(forcastCondition);
weatherData.setData(10,480,10);
System.out.println("========================================");
weatherData.removeObserver(currentCondition);
weatherData.setData(20,60,700);
}
}
6、运行结果
***** the day temperature is 10.0******
***** the day pressure is 480.0******
***** the day humidity is 10.0******
***** Tomorrow temperature is 10.0******
***** Tomorrow pressure is 480.0******
***** Tomorrow humidity is 10.0******
========================================
***** Tomorrow temperature is 20.0******
***** Tomorrow pressure is 60.0******
***** Tomorrow humidity is 700.0******
1、Java内置的观察者
Observable
Observer
2、用Java内置观察者重新设计该项目
3、内置观察者的注意点
Observable是类而不是接口
1、模板类实现OBserver接口
package cn;
import java.util.Observable;
import java.util.Observer;
public class CurrentCondition implements Observer{
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
@Override
public void update(Observable o, Object arg) {
this.temperature = ((WeatherData.Data)(arg)).getTemperature();
this.pressure = ((WeatherData.Data)(arg)).getPressure();
this.humidity = ((WeatherData.Data)(arg)).getHumidity();
display();
}
public void display(){
System.out.println("***** the day temperature is " + temperature + "******");
System.out.println("***** the day pressure is " + pressure + "******");
System.out.println("***** the day humidity is " + humidity + "******");
}
}
package cn;
import java.util.Observable;
import java.util.Observer;
public class ForcastCondition implements Observer {
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
@Override
public void update(Observable o, Object arg) {
this.temperature = ((WeatherData.Data)(arg)).getTemperature();
this.pressure = ((WeatherData.Data)(arg)).getPressure();
this.humidity = ((WeatherData.Data)(arg)).getHumidity();
display();
}
public void display(){
System.out.println("***** Tomorrow temperature is " + temperature + "******");
System.out.println("***** Tomorrow pressure is " + pressure + "******");
System.out.println("***** Tomorrow humidity is " + humidity + "******");
}
}
2、Weacher继承OBserverable接口
package cn;
import java.util.Observable;
public class WeatherData extends Observable{
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
public WeatherData() {
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
// 温度、湿度、气压发生改变时,触发函数
public void dataChange(){
this.setChanged();
this.notifyObservers(new Data(temperature,pressure,humidity));
}
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
class Data{
// 环境温度
private float temperature;
// 环境气压
private float pressure;
// 环境湿度
private float humidity;
public Data(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
@Override
public String toString() {
return "Data{" +
"temperature=" + temperature +
", pressure=" + pressure +
", humidity=" + humidity +
'}';
}
}
}
3、测试代码如下:
package cn;
public class Application {
public static void main(String[] args) {
CurrentCondition currentCondition = new CurrentCondition();
ForcastCondition forcastCondition = new ForcastCondition();
WeatherData weatherData = new WeatherData();
weatherData.addObserver(currentCondition);
weatherData.addObserver(forcastCondition);
weatherData.setData(111,222,333);
weatherData.deleteObserver(currentCondition);
weatherData.setData(1,2,3);
}
}
4、测试结果如下:
***** Tomorrow temperature is 111.0******
***** Tomorrow pressure is 222.0******
***** Tomorrow humidity is 333.0******
***** the day temperature is 111.0******
***** the day pressure is 222.0******
***** the day humidity is 333.0******
***** Tomorrow temperature is 1.0******
***** Tomorrow pressure is 2.0******
***** Tomorrow humidity is 3.0******