一、概述
观察者模式是一种行为设计模式,允许对象间存在一对多的依赖关系 ,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新 。在这种模式中,发生状态改变的对象被称为"主题"(Subject),依赖它的对象被称为"观察者"(Observer)。
观察者模式 (Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。
让我们通过一个简单的例子来实现观察者模式。假设我们有一个气象站(WeatherStation),需要向许多不同的显示设备(如手机App、网站、电子屏幕等)提供实时天气数据。
首先,我们需要创建一个Subject接口,表示主题:
java
/**
* 被观察对象(主题)
* @author: lemon
* @date: 2024/4/26 13:08
**/
public interface Subject {
//1.添加观察者
void registerObserver(Observer observer);
//2.移除观察者
void removeObserver(Observer observer);
//3.通知观察者
void notifyObserver();
}
接下来,我们创建一个Observer接口,表示观察者:
java
/**
* 观察者
* @author: lemon
* @date: 2024/4/26 13:08
**/
public interface Observer {
void update(float temperature);
}
现在,我们创建一个具体的主题,如WeatherStation,实现Subject接口:
java
/**
* @Description: 气象站(被观察对象)---主题
* @Author: lemon
* @CreateTime: 2024-04-26 13:15
* @Version: 1.0
*/
public class WeatherStation implements Subject{
//温度
private float temperature;
//1.创建一个集合,用来保存观察者
List<Observer> observerList;
public WeatherStation() {
observerList = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observerList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observerList.remove(observer);
}
//通知每一个客户端
@Override
public void notifyObserver() {
for (Observer observer : observerList) {
observer.update(temperature);
}
}
//更新温度
public void changeTemperature(float temperature){
this.temperature = temperature;
notifyObserver();
}
}
最后,我们创建两个具体的观察者,实现Observer接口:
java
public class AppClient implements Observer{
@Override
public void update(float temperature) {
System.out.println("气象站更新温度,App温度为:" + temperature);
}
}
public class WebClient implements Observer{
@Override
public void update(float temperature) {
System.out.println("气象站更新温度,网站温度为:" + temperature);
}
}
现在我们可以创建一个WeatherStation实例并向其注册观察者。当WeatherStation的数据发生变化时,两个观察者会收到通知并更新自己的显示。
java
public class Test {
public static void main(String[] args) {
//1.创建气象站和观察者
WeatherStation weatherStation = new WeatherStation();
Observer appClient = new AppClient();
Observer webClient = new WebClient();
//2.添加观察者
weatherStation.registerObserver(appClient);
weatherStation.registerObserver(webClient);
//3.气象站改变温度
weatherStation.changeTemperature(25.4F);
}
}
二、发布订阅
发布-订阅模式 和观察者模式 都是用于实现对象间的松耦合通信的设计模式。尽管它们具有相似之处,但它们在实现方式和使用场景上存在一些关键区别。他们在概念上有一定的相似性,都是用于实现对象间的松耦合通信 。可以将发布-订阅模式看作是观察者模式的一种变体或扩展。
两种模式的区别:
1、观察者模式:
观察者模式定义了一种一对多的依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖于它的对象(观察者)都会得到通知并自动更新。在这个模式中,被观察者和观察者之间存在直接的关联关系。观察者模式主要包括两类对象:被观察者(Subject)和观察者(Observer)。
2、发布订阅模式:
发布订阅模式引入了一个第三方组件(通常称为消息代理或事件总线) ,该组件负责维护发布者和订阅者之间的关系。这意味着发布者和订阅者彼此不直接通信 ,而是通过消息代理进行通信。这种间接通信允许发布者和订阅者在运行时动态地添加或删除,从而提高了系统的灵活性和可扩展性
还是使用上面的案例,用发布订阅的模式来实现:
首先,我们创建一个Subscriber
接口,用于表示订阅者
java
public interface Subscriber {
//当发生某件事情时,执行对应行为
void onEvent(Map<String, Object> event);
}
接下来,我们创建一个EventBus
类,用于管理发布者和订阅者之间的通信:
java
/**
* @Description: 消息代理
* @Author: lemon
* @CreateTime: 2024-04-26 15:42
* @Version: 1.0
*/
public class EventBus {
//维护消息类型和订阅者的关系
private final Map<String, List<Subscriber>> subscribers = new HashMap<>(2);
//添加观察者(订阅关系)
public void registerSubscriber(String eventType, Subscriber subscriber) {
List<Subscriber> subscriberList = subscribers.computeIfAbsent(eventType, k -> new ArrayList<>());
subscriberList.add(subscriber);
}
//移除观察者(解除订阅关系)
public void removeSubscriber(String eventType, Subscriber subscriber) {
List<Subscriber> subscriberList = subscribers.get(eventType);
if (subscriberList != null) {
subscriberList.remove(subscriber);
}
}
//发布消息(状态改变)
public void publishEvent(String eventType, Map<String, Object> event) {
List<Subscriber> subscriberList = subscribers.get(eventType);
if(subscriberList != null){
for (Subscriber subscriber : subscriberList) {
subscriber.onEvent(event);
}
}
}
}
然后,我们创建两个具体的订阅者实现:
java
public class AppSubscriber implements Subscriber{
@Override
public void onEvent(Map<String, Object> event) {
System.out.println("app已更新最新温度:" + event.get("tmp"));
}
}
public class WebSubscriber implements Subscriber{
@Override
public void onEvent(Map<String, Object> event) {
System.out.println("web已更新最新温度:" + event.get("tmp"));
}
}
最后,定义一个主题和客户端:
java
/**
* @Description: 气象站
* @Author: lemon
* @CreateTime: 2024-04-26 15:58
* @Version: 1.0
*/
public class WeatherStation {
private EventBus eventBus;
public WeatherStation(EventBus eventBus) {
this.eventBus = eventBus;
}
public void changeTemperature(float temperature){
Map<String, Object> event = new HashMap<>();
event.put("tmp", temperature);
eventBus.publishEvent("temperature", event);
}
public static void main(String[] args) {
//1.创建订阅者
AppSubscriber appSubscriber = new AppSubscriber();
WebSubscriber webSubscriber = new WebSubscriber();
//2.构建消息代理
EventBus bus = new EventBus();
bus.registerSubscriber("temperature", appSubscriber);
bus.registerSubscriber("temperature", webSubscriber);
//3.发布消息
WeatherStation weatherStation = new WeatherStation(bus);
weatherStation.changeTemperature(25.6F);
}
}
总结一下两者的区别:
- 通信方式:观察者模式中,观察者与被观察者之间存在直接的关联关系,而发布-订阅模式中,发布者和订阅者通过一个第三方组件(消息代理或事件总线)进行通信,彼此之间不存在直接关联关系。
- 系统复杂性:发布-订阅模式引入了一个额外的组件(消息代理或事件总线),增加了系统的复杂性,但同时也提高了系统的灵活性和可扩展性。
- 使用场景:观察者模式适用于需要将状态变化通知给其他对象的情况,而发布-订阅模式适用于事件驱动的系统,尤其是那些需要跨越多个模块或组件进行通信的场景。