文章目录
一、背景
本内容是来自书籍《Head First 设计模式》的第二章,观察者模式(让对象知悉你的现况)
二、OO基础
- 封装
- 继承
- 多态
- 抽象
三、OO设计原则
- 封装变化,将变化的部分封装起来,与固定不变的部分分开
- 面向接口编程,而非面向实现编程
- 组合优于继承
- 为交互对象之间的松耦合而努力
四、认识观察者模式
1.定义
观察者模式——定义了对象之间的一对多关系。这样一来,当一个对象的数据状态发生变化时,其他依赖它的对象都能感知到数据的变更
2.UML图
3.松耦合
观察者模式提供了一种对象设计,让主题和观察者之间松耦合(两个对象之间依然可以交互,但是互相不知道彼此的细节)。
因为主题不知道观察者的一切,它只知道观察者实现了某一个接口,主题不需要知道观察者是谁,它做了什么或其他任何细节。在不改动主题的情况,我们可以任意的增加观察者或者减少观察者,只需要让它实现某个接口就可以。主题的工作就是,当状态发生变更时,将数据通过某种协议发送给观察者。
松耦合的设计之所以能让我们建立一个有弹性的系统,是因为它使对象之间的依赖降到了最低。
五、气象站的设计
1.按照观察者模式的“推”方式实现
a.UML图
b.代码案例
(1)主题:
// subject
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// concreteSubject
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int index = observers.indexOf(observer);
if (index >= 0){
observers.remove(index);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature,humidity,pressure);
}
}
public void measurementsChanged(){
notifyObservers();
}
public void setMeasurement(float temperature,float humidity,float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
(2)观察者:
public interface Observer {
void update(float temperature,float humidity,float pressure);
}
//这里具体的观察者只展示一种,UML类图里的其他观察者实现是一样的
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
//设置展示板需要的数据
this.temperature = temperature;
this.humidity = humidity;
//进行展示
display();
}
@Override
public void display() {
System.out.println("Current conditions: "+temperature+"F degrees and "+humidity+"% humidity");
}
}
2.按照观察者模式的“拉”方式实现
a.UML
b.代码案例
“拉”的方式是使用Java内容观察者模式实现的,主题继承了java.util.Observable,观察者实现了java.util.Observer
public class WeatherDataV2 extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherDataV2() {
}
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
// 其他观察者实现原理一样,不做重复粘贴了。对于DisplayElement接口和“推”一样
public class CurrentConditionsDisplay implements Observer,DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherDataV2){
WeatherDataV2 weatherDataV2 = (WeatherDataV2) o;
this.temperature = weatherDataV2.getTemperature();
this.humidity = weatherDataV2.getHumidity();
}
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
3.“推” & “拉”的优劣
(1)“推”的优点:它将全部数据都通知给了观察者,这样可以减少观察者获取数据的繁琐性
(2)“推”的缺点:当我们需要修改参数(数据)的时候,例如新增几个指标,那么这时候,我们必须修改协议,更新Observer接口中的参数个数,并且将其每个子类都要修改一遍。
(3)“拉”的优点:不必为主题新增参数或减少参数担忧,观察者主动去获取自己需要的数据。
(4)“拉”的缺点:获取数据比较繁琐
其实两者优缺点是相反的,弥补的双方的劣势,担忧不可避免的带来自己的劣势。一般来讲,人们认为“推”的方式是正确的(书上说的)
六、总结
1.观察者模式遵循了的设计原则
- 封装变化,将变化的部分封装起来,并且与固定不变的部分分开。 在观察者模式中,会改变的是主题的状态以及观察者的数目和类型。用这个模式,我们可以改变依赖主题的对象却不用修改主题。
- 面向接口编程,而非针对实现编程。 主题和观察者都使用接口,观察者使用主题的接口向主题注册,而主题使用观察者的接口向观察者通知数据,两者不必关心对方的实现细节,这样可以达到松耦合的目的。
- 组合优于继承。 观察者模式利用“组合”的方式将观察者注册进主题,对象之间的关系不是通过继承来实现的,而是在运行时通过组合的方式实现的。
- 为了交互对象之间松耦合而努力。 同2
2.观察者模式的一些要点纪要
- 观察者模式定义了一对多的依赖关系。
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 观察者和可观察者之间松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
- 使用此模式时,我们可从观察者处“推”或者“拉”的方式获取数据。
- 有多个观察者时,不可以依赖特定的通知次序。
- Java有多种观察者模式的实现,包括了通用的java.util.Observable,要注意java.util.Observable实现所带来的问题,因为它改变的观察者的通知顺序。如果有必要的话,我们可以实现自己的Observable,其实并不难。
- 许多GUI框架、javaBeans、RMI都使用了观察者模式。