目录
观察者模式介绍
气象站
最初的气象应用
布告板接口
布告板实现
气象台监控
测试
布告板发展
气象站观察者模式
测试
Java内置实现观察者
WeatherData
Display
CurrentDisplay
Current2Display
Test
输出结果
观察者模式定义了对象之间的一对多的依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
下面通过气象站的事例来介绍观察者模式
最开始气象站有三个感应装置,分别为温度(temperature)、湿度(humidity)和气压(pressure),显示装置也就只有一个布告板(具体如下),气象站的三个感应装置获取到的最新数据后要展示在布告板上,因此我们可能设计如下的实现:
当前天气情况:
气温:25℃
湿度:60
气压:1atm
public interface Display {
void display (double temperature, double humidity, double pressure);
}
public class CurrentDisplay implements Display {
public void display (double temperature, double humidity, double pressure) {
String content = String.format("当前天气情况:\n气温:%s℃\n湿度:%s\n气压:%satm", temperature, humidity, pressure);
System.out.println(content);
}
}
public class WeatherData {
private double temperature;
private double humidity;
private double pressure;
public void weatherDataChange(double temperature, double humidity, double pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
updateDisplay();
}
private void updateDisplay() {
new CurrentDisplay().display(temperature, humidity, pressure);
}
}
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
weatherData.weatherDataChange(25D, 72D, 1.1D);
}
}
输出结果
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
随着业务的发展,布告板的展现形式也越来越多种多样,按照上述的形式,我们只能在WeatherData:updateDisplay()下面不断的新增需要通知的布告板,这个看起来没有什么问题,但是细想下每次新增一个布告板,我们都需要在WeatherData:updateDisplay()下面新加一个通知,没移除一个布告板,都需要将WeatherData:updateDisplay()下面对应的进行移除,天气数据和布告板紧紧的耦合在一起,这个并不符合我们的高内聚低耦合的设计原则,因此我们需要再次进行优化,设计为观察者模式。
针对上述,布告板我们无需进行改变,我们对WeatherData进行优化,具体如下(这里我们只介绍设计模式,不考虑线程安全):
public class WeatherData {
private double temperature;
private double humidity;
private double pressure;
private List displays = new ArrayList();
//注册观察者
public void addDisplay(Display display) {
displays.add(display);
}
//取消注册
public void removeDisplay(Display display) {
int i = displays.indexOf(display);
if (i >= 0) {
displays.remove(display);
}
}
public void weatherDataChange(double temperature, double humidity, double pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
updateDisplay();
}
private void updateDisplay() {
for (Display display : displays) {
display.display(temperature, humidity, pressure);
}
}
}
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
Display display1 = new CurrentDisplay();
Display display2 = new Current2Display();
Display display3 = new CurrentDisplay();
weatherData.addDisplay(display1);
weatherData.addDisplay(display2);
weatherData.addDisplay(display3);
System.out.println("----------注册三个观察者----------");
weatherData.weatherDataChange(25D, 72D, 1.1D);
weatherData.removeDisplay(display3);
System.out.println("----------取消第三个观察者----------");
weatherData.weatherDataChange(28D, 77D, 0.9D);
}
}
输出结果
----------注册三个观察者----------
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况2:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
----------取消第三个观察者----------
当前天气情况:
气温:28.0℃
湿度:77.0
气压:0.9atm
当前天气情况2:
气温:28.0℃
湿度:77.0
气压:0.9atm
这样我们就把布告板从气象台中抽象出来,后续再添加布告板,我们也无需再次修改WeatherData代码。
Java内置中提供了 java.util.Observer接口和java.util.Observable类来实现观察者模式,下面我们就使用Java内置工具来实现上述气象台的观察者模式。
package com.lulei.study.design.patterns.observer;
import lombok.Data;
import java.util.Observable;
/**
* Created by lulei on 2019/4/16.
*/
@Data
public class WeatherData extends Observable{
private double temperature;
private double humidity;
private double pressure;
public void weatherDataChange(double temperature, double humidity, double pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
updateDisplay();
}
private void updateDisplay() {
//在调用notifyObservers()之前,先调用setChanged()来指示状态已经改变
setChanged();
//这里我们没有传送数据,表示我们采用的做法是拉
notifyObservers();
}
}
package com.lulei.study.design.patterns.observer;
/**
* Created by lulei on 2019/4/16.
*/
public interface Display {
void display (double temperature, double humidity, double pressure);
}
package com.lulei.study.design.patterns.observer;
import java.util.Observable;
import java.util.Observer;
/**
* Created by lulei on 2019/4/16.
*/
public class CurrentDisplay implements Observer, Display {
public CurrentDisplay(Observable observable) {
observable.addObserver(this);
}
public void display (double temperature, double humidity, double pressure) {
String content = String.format("当前天气情况:\n气温:%s℃\n湿度:%s\n气压:%satm", temperature, humidity, pressure);
System.out.println(content);
}
public void update(Observable observable, Object arg) {
if (observable instanceof WeatherData) {
WeatherData weatherData = (WeatherData)observable;
display(weatherData.getTemperature(), weatherData.getHumidity(), weatherData.getPressure());
}
}
}
package com.lulei.study.design.patterns.observer;
import java.util.Observable;
import java.util.Observer;
/**
* Created by lulei on 2019/4/16.
*/
public class Current2Display implements Observer, Display {
public Current2Display(Observable observable) {
observable.addObserver(this);
}
public void display (double temperature, double humidity, double pressure) {
String content = String.format("当前天气情况2:\n气温:%s℃\n湿度:%s\n气压:%satm", temperature, humidity, pressure);
System.out.println(content);
}
public void update(Observable observable, Object arg) {
if (observable instanceof WeatherData) {
WeatherData weatherData = (WeatherData)observable;
display(weatherData.getTemperature(), weatherData.getHumidity(), weatherData.getPressure());
}
}
}
package com.lulei.study.design.patterns.observer;
import java.util.Observer;
/**
* Created by lulei on 2019/4/16.
*/
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
Observer display1 = new CurrentDisplay(weatherData);
Observer display2 = new Current2Display(weatherData);
Observer display3 = new CurrentDisplay(weatherData);
System.out.println("----------注册三个观察者----------");
weatherData.weatherDataChange(25D, 72D, 1.1D);
weatherData.deleteObserver(display3);
System.out.println("----------取消第三个观察者----------");
weatherData.weatherDataChange(28D, 77D, 0.9D);
}
}
----------注册三个观察者----------
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况2:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
----------取消第三个观察者----------
当前天气情况2:
气温:28.0℃
湿度:77.0
气压:0.9atm
当前天气情况:
气温:28.0℃
湿度:77.0
气压:0.9atm