观察者模式由来
观察者模式(Observer Pattern)是一种行为型设计模式,它的起源可以追溯到20世纪90年代初,由设计模式四人帮(Erich Gamma, Richard Helm, Ralph Johnson 和 John Vlissides)在其著作《设计模式:可复用面向对象软件的基础》中首次提出。观察者模式用于解决对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。
概念
- 被观察者(Subject):定义一个接口,用于添加、删除和通知观察者。
- 观察者(Observer):定义一个接口,用于接收被观察者的通知并执行相应的操作。
- 具体被观察者(ConcreteSubject):实现被观察者接口,维护观察者列表,并在状态改变时通知所有观察者。
- 具体观察者(ConcreteObserver):实现观察者接口,具体实现接收到通知后的操作。
实现原理
观察者模式的核心原理是通过将对象间的依赖关系从硬编码转移到外部,使得一个对象(被观察者)可以在不通知其他对象的情况下更改其状态,然后在适当的时候通知所有依赖于它的对象(观察者)。这种解耦的设计方式使得代码更加灵活,易于扩展和维护。
我有一个朋友张三,他总是关心天气情况,每天会看天气预报,在这个过程中,天气预报(被观察者)和张三(观察者)之间就会存在一种依赖关系。当天气预报发生变化时,张三需要得到通知并及时更新自己的信息。
定义角色:
- 被观察者(Subject):天气预报。它包含了当前的天气状况以及未来一段时间内的天气预报信息。
- 观察者(Observer):张三。他是一个依赖于天气预报信息的用户。
建立依赖关系:
- 张三订阅了天气预报服务,这样当他打开电视或查看手机时,就能接收到最新的天气预报信息。
事件通知机制:
- 天气预报服务会在天气状况发生变化时,或者新的预报信息生成时,触发通知机制。这个机制负责将最新的天气信息发送给所有订阅了服务的用户,包括张三。
更新策略:
- 张三在接收到天气预报信息后,会根据信息的内容更新自己的认知,比如决定是否要带伞、穿什么衣服等。
动态加入和退出:
- 如果张三决定不再订阅天气预报服务,他可以随时取消订阅。同样,如果张三从一个城市搬到另一个城市,他可以订阅新的城市的天气预报服务。
技术实现
首先,我们定义一个Subject
接口和一个Observer
接口:
javascript
// 被观察者
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者
public interface Observer {
void update(String message);
}
然后,我们创建一个WeatherForecast
类作为被观察者,实现Subject
接口:
javascript
import java.util.ArrayList;
import java.util.List;
public class WeatherForecast implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
}
接下来,我们创建一个WeatherWatcher
类作为观察者,实现Observer
接口:
javascript
public class WeatherWatcher implements Observer {
private String name;
public WeatherWatcher(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received weather forecast: " + message);
}
}
最后,我们在主函数中创建一个WeatherForecast
对象和两个WeatherWatcher
对象,并让它们订阅天气预报:
javascript
public static void main(String[] args) {
WeatherForecast weatherForecast = new WeatherForecast();
WeatherWatcher watcher1 = new WeatherWatcher("张三");
WeatherWatcher watcher2 = new WeatherWatcher("李四");
weatherForecast.registerObserver(watcher1);
weatherForecast.registerObserver(watcher2);
weatherForecast.setMessage("今天天气晴朗,温度适中。");
weatherForecast.setMessage("明天将会有大雨,请携带雨具。");
}
运行这个程序,你会看到张三和李四都收到了天气预报的通知。
Spring 实现
定义事件类:首先,我们需要定义一个事件类,它将携带被观察者状态变化的信息。
javascript
package com.neo.design.observer;
import org.springframework.context.ApplicationEvent;
public class WeatherEvent extends ApplicationEvent {
private String weatherInfo;
public WeatherEvent(Object source, String weatherInfo) {
super(source);
this.weatherInfo = weatherInfo;
}
public String getWeatherInfo() {
return weatherInfo;
}
}
- 创建事件发布者 :接下来,我们创建一个事件发布者,它将负责发布天气变更事件。在这个例子中,我们将使用Spring的
ApplicationEventPublisher
来发布事件。
javascript
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class WeatherEventPublisher {
private final ApplicationEventPublisher publisher;
public WeatherEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void publishWeatherChangeEvent(String message) {
publisher.publishEvent(new WeatherChangeEvent(message));
}
}
创建事件监听器 :然后,我们创建一个事件监听器,它将实现ApplicationListener
接口,并重写onApplicationEvent
方法。在这个方法中,我们将处理天气变更事件,并通知相关的观察者。
javascript
package com.neo.design.observer;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class WeatherEventPublisher {
private final ApplicationEventPublisher publisher;
public WeatherEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void publishWeatherChangeEvent(String message) {
publisher.publishEvent(new WeatherChangeEvent(message));
}
}
创建用户服务:我们还需要创建一个用户服务,它将负责管理用户的订阅信息,并在接收到天气变更事件时通知用户。
javascript
package com.neo.design.observer;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserService {
private final List<String> subscribers = new ArrayList<>();
public void subscribe(String subscriber) {
subscribers.add(subscriber);
}
public void notifySubscribers(String message) {
for (String subscriber : subscribers) {
System.out.println(subscriber + " received weather forecast: " + message);
}
}
}
创建控制器:最后,我们创建一个控制器,它将接收用户订阅请求和天气变更请求,并调用相应的服务来处理这些请求。
javascript
package com.neo.design.observer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WeatherForecastController {
@Autowired
private UserService userService;
@Autowired
private WeatherEventPublisher publisher;
@PostMapping("/subscribe")
public String subscribe(@RequestParam("subscriber") String subscriber) {
userService.subscribe(subscriber);
return "Subscriber added!";
}
@PostMapping("/update-weather")
public String updateWeather(@RequestParam("message") String message) {
publisher.publishWeatherChangeEvent(message);
return "Weather updated!";
}
}
通过以上设计,我们利用Spring Boot的事件机制和依赖注入特性实现了一个高效的观察者模式。
验证
新增一名观察者
设定一个被观察者所关注的消息。
执行功能,返回测试结果如下
总结
观察者模式(Observer Pattern)在软件工程设计中扮演着重要角色,观察者模式实现了发布者(主题)和订阅者(观察者)之间的松散耦合。发布者无需知道具体的订阅者是谁,只需要维护一个订阅者列表,并在状态变化时通知它们。这种解耦使得系统更具灵活性和可扩展性。通过观察者模式,添加或移除订阅者非常容易,不需要修改发布者的代码。只需实现观察者接口并注册或取消注册即可。这使得系统在需求变化或扩展时更易于维护。它适用于各种需要实时更新和异步处理的场景,提升了系统的响应能力和用户体验,是设计模式中一个非常实用且常用的模式。