观察者模式

概述

观察者模式是一种行为设计模式,用于在对象之间建立一种一对多的依赖关系,当一个对象状态发生改变时,其所有依赖者都可以得到通知并自动更新。

通俗来说,观察者模式就是一个主题和一群订阅者之间的关系,主题会维护一个订阅者列表,党主体状态发生变化时,他会通知所有订阅者进行相应的更新操作。

使用场景

  1. 当一个对象改变需要同时改变其他对象,并不确定有多少对象要改变时。
  2. 当一个对象需要将自己的改变通知给其他对象,而又不希望与其紧密耦合时。

优缺点

  1. 优点

    • 解耦:观察者模式将主题和观察者解耦,使它们可以独立变化,互不影响。
    • 扩展性:可以灵活地添加新的观察者,或者改变观察者的行为,而无需修改主题的代码。
    • 维护一致性:主题和观察者之间建立了一种松散的依赖关系,保证了一致性。
  2. 缺点

    • 过多的通知:当主题对象有大量的观察者时,每次状态变化都需要通知所有观察者,可能会造成性能问题。
    • 循环引用:当观察者之间相互引用时,可能导致循环引用的问题,需要注意避免。

总结来说,观察者模式适用于需要实现对象间的动态一对多关系,并且希望避免紧耦合的场景。它可以帮助我们实现松散耦合的设计,提高代码的可维护性和扩展性。

结构

  • 主题Subject: 一般会定义成一个接口,提供方法用于注册、删除和通知观察者,通常也包含一个状态,当状态发生改变时,通知所有的观察者。
  • 观察者Observer: 观察者也需要实现一个接口,包含一个更新方法,在接收主题通知时执行对应的操作。
  • 具体主题ConcreteSubject: 主题的具体实现, 维护一个观察者列表,包含了观察者的注册、删除和通知方法。
  • 具体观察者ConcreteObserver: 观察者接口的具体实现,每个具体观察者都注册到具体主题中,当主题状态变化并通知到具体观察者,具体观察者进行处理。

示例

假设有一个小镇,这个小镇上有一家天气预报中心。每天早上,天气预报中心会发布当天的天气情况,包括温度、湿度、风力等信息。同时,小镇上的几个居民对天气情况非常关注,因为天气会影响他们的日常生活。

在这个故事中,天气预报中心是被观察者(主题),而居民们是观察者。他们想要根据天气情况来做出相应的安排,比如选择穿什么衣服、是否要带伞等。

现在,我们来看一下如何使用观察者模式来实现这个故事。

首先定义一个主题接口,其中包含了添加观察者,移除观察者和通知观察者的方法。

1
2
3
4
5
public interface WeatherCenter {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}

然后,我们定义一个具体的天气预报中心(ConcreteWeatherCenter)类,实现了主题接口,并维护了一个观察者列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ConcreteWeatherCenter implements WeatherCenter {
private List<Observer> observers = new ArrayList<>();
private WeatherData weatherData;

public void addObserver(Observer observer) {
observers.add(observer);
}

public void removeObserver(Observer observer) {
observers.remove(observer);
}

public void notifyObservers() {
for (Observer observer : observers) {
observer.update(weatherData);
}
}

public void setWeatherData(WeatherData weatherData) {
this.weatherData = weatherData;
}
}

天气数据类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class WeatherData {

private Double temperature;

private Double humidity;

private Integer windSpeed;

public Double getTemperature() {
return temperature;
}

public void setTemperature(Double temperature) {
this.temperature = temperature;
}

public Double getHumidity() {
return humidity;
}

public void setHumidity(Double humidity) {
this.humidity = humidity;
}

public Integer getWindSpeed() {
return windSpeed;
}

public void setWindSpeed(Integer windSpeed) {
this.windSpeed = windSpeed;
}
}

接下来定义观察者接口

1
2
3
public interface Observer {
void update(WeatherData weatherData);
}

实现一个具体的居民类,实现了观察者的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteResident implements Observer {

private String name;

public ConcreteResident(String name) {
this.name = name;
}

@Override
public void update(WeatherData weatherData) {
System.out.println(name + "今天的气温是:" + weatherData.getTemperature());
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class main {

public static void main(String[] args) {
ConcreteWeatherCenter concreteWeatherCenter = new ConcreteWeatherCenter();

ConcreteResident lisa = new ConcreteResident("Lisa");
ConcreteResident tom = new ConcreteResident("Tom");
concreteWeatherCenter.addObserver(lisa);
concreteWeatherCenter.addObserver(tom);

WeatherData weatherData = new WeatherData();
weatherData.setTemperature(29.0);
weatherData.setHumidity(84.0);
weatherData.setWindSpeed(2);

concreteWeatherCenter.setWeatherData(weatherData);
concreteWeatherCenter.notifyObservers();
}
}

在这个故事中,天气预报中心是主题,居民们是观察者。当天气预报中心发布天气信息时,所有的居民都会收到通知,并根据天气情况做出相应的安排。

这个故事中的观察者模式展示了一个实际生活中常见的场景。通过观察者模式,天气预报中心和居民之间建立了一种松散的依赖关系,使得天气预报中心能够方便地通知居民们最新的天气情况,居民们也能够根据天气情况做出适当的调整。

故事中的观察者模式的优点是:

天气预报中心和居民之间实现了解耦,它们可以独立变化,互不影响。
天气预报中心可以方便地添加或移除观察者,居民们也可以灵活地订阅或取消订阅天气通知。
天气预报中心发布天气信息时,所有的居民都能够及时收到通知,确保信息的一致性。
观察者模式的缺点在这个故事中并不明显,但在实际应用中可能存在以下问题:

如果观察者过多或者观察者执行的操作较为耗时,可能会导致性能问题。
观察者之间相互引用时,可能出现循环引用的问题,需要注意避免。
总结来说,观察者模式在实际应用中非常有用,可以帮助我们实现对象之间的一对多关系,实现解耦、灵活性和一致性。无论是天气预报、事件通知还是其他需要实现观察者和被观察者之间关系的场景,观察者模式都是一个强大的工具。