这里引入例子,当前有几个硬件设备,负责收集当前温度,湿度和气压,WeatherData对象从气象站收集数据,然后WeatherData向显示器发送数据,对应显示面板解析数据后显示(计算原始数据,输出日常所见的数据)
由于各种场合的需要,有的人只需要看到气象状况,有的人需要看到天气预报,所有根据需要显示不同信息,显示到不同的面板
对应代码实现
DisplayElement
package headfirst.hd.observer.old.interfaces;
public interface DisplayElement {
void display(float temperature, float humidity, float pressure);
}
CurrentConditionsDisplay
package headfirst.hd.observer.old.impl;
import headfirst.hd.observer.old.interfaces.DisplayElement;
//当前天气状况
public class CurrentConditionsDisplay implements DisplayElement {
@Override
public void display(float temperature, float humidity, float pressure) {
System.out.println("========当前天气状况=========");
System.out.println("温度:" + temperature);
System.out.println("湿度:" + humidity);
System.out.println("压强:" + pressure);
System.out.println("========当前天气状况=========");
}
}
ForecastDisplay
package headfirst.hd.observer.old.impl;
import java.util.Random;
import headfirst.hd.observer.old.interfaces.DisplayElement;
//天气预报
public class ForecastDisplay implements DisplayElement{
@Override
public void display(float temperature, float humidity, float pressure) {
System.out.println("========天气预报=========");
//天气预报计算是一个相当复杂的过程,这里简单处理
int randomNum = new Random().nextInt(10);
randomNum %= 3;
if (randomNum == 0) {
System.out.println("天气晴朗");
} else if (randomNum == 1) {
System.out.println("暴雨");
} else if (randomNum == 2) {
System.out.println("多雨转晴");
}
System.out.println("========天气预报=========");
}
}
StatisticsDisplay
package headfirst.hd.observer.old.impl;
import headfirst.hd.observer.old.interfaces.DisplayElement;
//天气统计
public class StatisticsDisplay implements DisplayElement {
@Override
public void display(float temperature, float humidity, float pressure) {
System.out.println("========当前天气状况=========");
//天气统计是一个相当复杂的过程,这里简单处理
System.out.println("未来几天,多云,适合外出");
System.out.println("========当前天气状况=========");
}
}
WeatherData
package headfirst.hd.observer.old;
import headfirst.hd.observer.old.impl.CurrentConditionsDisplay;
import headfirst.hd.observer.old.impl.ForecastDisplay;
import headfirst.hd.observer.old.impl.StatisticsDisplay;
import headfirst.hd.observer.old.interfaces.DisplayElement;
public class WeatherData {
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measuremetnsChanged();
}
//方法任务单一原则
public void measuremetnsChanged() {
DisplayElement element1 = new CurrentConditionsDisplay();
DisplayElement element2 = new ForecastDisplay();
DisplayElement element3 = new StatisticsDisplay();
element1.display(temperature, humidity, pressure);
element2.display(temperature, humidity, pressure);
element3.display(temperature, humidity, pressure);
}
}
测试
DriveTest
package headfirst.hd.observer.old.test;
import headfirst.hd.observer.old.WeatherData;
public class DriveTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
//这里通过设置weatherData数据,代替硬件收集数据
weatherData.setMeasurements(20, 30.2f, 59);
weatherData.setMeasurements(26, 20.4f, 69);
weatherData.setMeasurements(10, 36f, 94);
}
}
运行结果
========当前天气状况=========
温度:20.0
湿度:30.2
压强:59.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
========当前天气状况=========
未来几天,多云,适合外出
========当前天气状况=========
========当前天气状况=========
温度:26.0
湿度:20.4
压强:69.0
========当前天气状况=========
========天气预报=========
天气晴朗
========天气预报=========
========当前天气状况=========
未来几天,多云,适合外出
========当前天气状况=========
========当前天气状况=========
温度:10.0
湿度:36.0
压强:94.0
========当前天气状况=========
========天气预报=========
暴雨
========天气预报=========
========当前天气状况=========
未来几天,多云,适合外出
========当前天气状况=========
当前设计满足预期需求
当前设计为,WeatherData为管理数据与面板管理对象,且管理多个显示面板对象,一对多关系。容易引发的问题是,当面板(多)数据增加或者减少时,我们必须去修改WeatherData(一)代码,我们在设计代码时候希望对象之间是松耦合设计,及当某一个对象修改时候,不影响和其有关系的代码修改。
及简单抽象为以下三个问题
以后添加面板,怎么才不影响现有代码呢?
当我们管理组数时候,有一批数据需要添加或者删除时候,我们有下面的思路
整个流程都不会出现以上三个问题
- 当我们需要添加面板时候,不修改WeatherData中代码
- 当我们需要坚守面板时候,不修改WeatherData中代码
- 当我们修改WeatherData代码时候,不影响任何面板代码
解决办法,采用一定的数据结构解决该问题
对应代码
DisplayElement
package headfirst.hd.observer.news.interfaces;
public interface DisplayElement {
void display(float temperature, float humidity, float pressure);
}
CurrentConditionsDisplay
package headfirst.hd.observer.news.impl;
import headfirst.hd.observer.news.interfaces.DisplayElement;
public class CurrentConditionsDisplay implements DisplayElement {
@Override
public void display(float temperature, float humidity, float pressure) {
System.out.println("========当前天气状况=========");
System.out.println("温度:" + temperature);
System.out.println("湿度:" + humidity);
System.out.println("压强:" + pressure);
System.out.println("========当前天气状况=========");
}
}
ForecastDisplay
package headfirst.hd.observer.news.impl;
import java.util.Random;
import headfirst.hd.observer.news.interfaces.DisplayElement;
//天气预报
public class ForecastDisplay implements DisplayElement{
@Override
public void display(float temperature, float humidity, float pressure) {
System.out.println("========天气预报=========");
//天气预报计算是一个相当复杂的过程,这里简单处理
int randomNum = new Random().nextInt(10);
randomNum %= 3;
if (randomNum == 0) {
System.out.println("天气晴朗");
} else if (randomNum == 1) {
System.out.println("暴雨");
} else if (randomNum == 2) {
System.out.println("多雨转晴");
}
System.out.println("========天气预报=========");
}
}
StatisticsDisplay
package headfirst.hd.observer.news.impl;
import headfirst.hd.observer.news.interfaces.DisplayElement;
//天气统计
public class StatisticsDisplay implements DisplayElement {
@Override
public void display(float temperature, float humidity, float pressure) {
System.out.println("========当前天气状况=========");
//天气统计是一个相当复杂的过程,这里简单处理
System.out.println("未来几天,多云,适合外出");
System.out.println("========当前天气状况=========");
}
}
WeatherData
package headfirst.hd.observer.news;
import java.util.ArrayList;
import headfirst.hd.observer.news.interfaces.DisplayElement;
public class WeatherData {
private float temperature;
private float humidity;
private float pressure;
private ArrayList observers = new ArrayList();
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measuremetnsChanged();
}
//方法任务单一原则
public void measuremetnsChanged() {
notifyObservers();
}
public void registerObserver(DisplayElement observer) {
observers.add(observer);
}
public void removeObserver(DisplayElement observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (DisplayElement observer : observers) {
observer.display(temperature, humidity, pressure);
}
}
}
测试
DriveTest
package headfirst.hd.observer.news.test;
import headfirst.hd.observer.news.WeatherData;
import headfirst.hd.observer.news.impl.CurrentConditionsDisplay;
import headfirst.hd.observer.news.impl.ForecastDisplay;
public class DriveTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
//当前天气状态
CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay();
weatherData.registerObserver(conditionsDisplay);
//天气预报
ForecastDisplay forecastDisplay = new ForecastDisplay();
weatherData.registerObserver(forecastDisplay);
//这里通过设置weatherData数据,代替硬件收集数据
weatherData.setMeasurements(20, 30.2f, 59);
weatherData.setMeasurements(26, 20.4f, 69);
weatherData.setMeasurements(10, 36f, 94);
}
}
运行结果
========当前天气状况=========
温度:20.0
湿度:30.2
压强:59.0
========当前天气状况=========
========天气预报=========
天气晴朗
========天气预报=========
========当前天气状况=========
温度:26.0
湿度:20.4
压强:69.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
========当前天气状况=========
温度:10.0
湿度:36.0
压强:94.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
解决以上三个问题
- 当我们需要添加面板时候,不修改WeatherData中代码
- 当我们需要坚守面板时候,不修改WeatherData中代码
- 当我们修改WeatherData代码时候,不影响任何面板代码
为交互对象之间的松耦合设计,这里例子是一对多关系,涉及交互对象
例子一
例子二
微博关注,如果你关注某人,你就会收到这个人的动态消息
例子三
当我们下载某款Android app时,我们成为了这款app的观察者,当app升级或者有什么动态时候,app会向我们推向消息
红色为系统内置文件
WeatherData
package headfirst.hd.observer.java.impl;
import java.util.Observable;
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measuremetnsChanged();
}
//方法任务单一原则
public void measuremetnsChanged() {
//java内置方法有一个状态字段
this.setChanged();
notifyObservers();
}
}
对应代码
CurrentConditionsDisplay
package headfirst.hd.observer.java.impl;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer {
@Override
public void update(Observable paramObservable, Object paramObject) {
if (paramObservable instanceof WeatherData) {
//拿到可观察者对象
WeatherData weatherData = (WeatherData) paramObservable;
float temperature = weatherData.getTemperature();
float humidity = weatherData.getHumidity();
float pressure = weatherData.getPressure();
System.out.println("========当前天气状况=========");
System.out.println("温度:" + temperature);
System.out.println("湿度:" + humidity);
System.out.println("压强:" + pressure);
System.out.println("========当前天气状况=========");
}
}
}
ForecastDisplay
package headfirst.hd.observer.java.impl;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
//天气预报
public class ForecastDisplay implements Observer {
@Override
public void update(Observable paramObservable, Object paramObject) {
if (paramObservable instanceof WeatherData) {
//拿到可观察者对象
WeatherData weatherData = (WeatherData) paramObservable;
float temperature = weatherData.getTemperature();
float humidity = weatherData.getHumidity();
float pressure = weatherData.getPressure();
System.out.println("========天气预报=========");
//天气预报计算是一个相当复杂的过程,这里简单处理
int randomNum = new Random().nextInt(10);
randomNum %= 3;
if (randomNum == 0) {
System.out.println("天气晴朗");
} else if (randomNum == 1) {
System.out.println("暴雨");
} else if (randomNum == 2) {
System.out.println("多雨转晴");
}
System.out.println("========天气预报=========");
}
}
}
StatisticsDisplay
package headfirst.hd.observer.java.impl;
import java.util.Observable;
import java.util.Observer;
import headfirst.hd.observer.news.interfaces.DisplayElement;
//天气统计
public class StatisticsDisplay implements Observer {
@Override
public void update(Observable paramObservable, Object paramObject) {
if (paramObservable instanceof WeatherData) {
//拿到可观察者对象
WeatherData weatherData = (WeatherData) paramObservable;
float temperature = weatherData.getTemperature();
float humidity = weatherData.getHumidity();
float pressure = weatherData.getPressure();
System.out.println("========当前天气状况=========");
//天气统计是一个相当复杂的过程,这里简单处理
System.out.println("未来几天,多云,适合外出");
System.out.println("========当前天气状况=========");
}
}
}
测试类
DriveTest
package headfirst.hd.observer.java.test;
import headfirst.hd.observer.java.impl.CurrentConditionsDisplay;
import headfirst.hd.observer.java.impl.ForecastDisplay;
import headfirst.hd.observer.java.impl.WeatherData;
public class DriveTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
//当前天气状态
CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay();
weatherData.addObserver(conditionsDisplay);
//天气预报
ForecastDisplay forecastDisplay = new ForecastDisplay();
weatherData.addObserver(forecastDisplay);
//这里通过设置weatherData数据,代替硬件收集数据
weatherData.setMeasurements(20, 30.2f, 59);
weatherData.setMeasurements(26, 20.4f, 69);
//weatherData.setMeasurements(10, 36f, 94);
}
}
运行结果
========天气预报=========
天气晴朗
========天气预报=========
========当前天气状况=========
温度:20.0
湿度:30.2
压强:59.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
========当前天气状况=========
温度:26.0
湿度:20.4
压强:69.0
========当前天气状况=========
满足预期需求设计,符合
Observer(观察者实现的接口)
package java.util;
public abstract interface Observer
{
public abstract void update(Observable paramObservable, Object paramObject);
}
Observable(可观察者继承的类)
package java.util;
public class Observable
{
private boolean changed = false;
private Vector obs = new Vector();
public synchronized void addObserver(Observer paramObserver)
{
if (paramObserver == null) {
throw new NullPointerException();
}
if (!this.obs.contains(paramObserver)) {
this.obs.addElement(paramObserver);
}
}
public synchronized void deleteObserver(Observer paramObserver)
{
this.obs.removeElement(paramObserver);
}
public void notifyObservers()
{
notifyObservers(null);
}
public void notifyObservers(Object paramObject)
{
Object[] arrayOfObject;
synchronized (this)
{
if (!this.changed) {
return;
}
arrayOfObject = this.obs.toArray();
clearChanged();
}
for (int i = arrayOfObject.length - 1; i >= 0; i--) {
((Observer)arrayOfObject[i]).update(this, paramObject);
}
}
public synchronized void deleteObservers()
{
this.obs.removeAllElements();
}
protected synchronized void setChanged()
{
this.changed = true;
}
protected synchronized void clearChanged()
{
this.changed = false;
}
public synchronized boolean hasChanged()
{
return this.changed;
}
public synchronized int countObservers()
{
return this.obs.size();
}
}
当changed字段为false时候,以后方法不会通知可观察者
public void notifyObservers(Object paramObject)
{
Object[] arrayOfObject;
synchronized (this)
{
if (!this.changed) {
return;
}
arrayOfObject = this.obs.toArray();
clearChanged();
}
for (int i = arrayOfObject.length - 1; i >= 0; i--) {
((Observer)arrayOfObject[i]).update(this, paramObject);
}
}
所有在WeatherData类中,方法为以下实现
//方法任务单一原则
public void measuremetnsChanged() {
//java内置方法有一个状态字段
this.setChanged();
notifyObservers();
}
温度变化1度,通知
public void setMeasurements(float temperature, float humidity, float pressure) {
//简单处理
if (temperature - this.temperature > 1) {
this.setChanged();
}
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measuremetnsChanged();
}
//方法任务单一原则
public void measuremetnsChanged() {
notifyObservers();
}