设计模式(十八):观察者模式(Observer Pattern)

Published on 2024-04-07 09:17 in 分类: 博客 with 狂盗一枝梅
分类: 博客

一、观察者模式定义

观察者模式Observer Pattern)也叫做发布订阅模式Publish/subscribe),它是一个在项目中经常使用的模式,它是一种行为型设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。其定义如下:

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。(Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.)

观察者模式通用类图如下

观察者模式通用类图

观察者模式中的几种角色:

Subject被观察者:定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。

Observer观察者:观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。

ConcreteSubject具体的被观察者:定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

ConcreteObserver具体的观察者:每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

其实个人来说,我觉得“发布订阅模式”要比“观察者模式”的叫法更贴合它的意思,比如观察者模式在现实世界中的例子:报纸订阅,如果你订阅了一份杂志或报纸, 那就不需要再去报摊查询新出版的刊物了。 出版社 (即应用中的 “发布者”) 会在刊物出版后 (甚至提前) 直接将最新一期寄送至你的邮箱中。我们称这个过程叫做“订阅”,而非“观察”,所以我觉得应该叫“发布订阅模式”。

二、观察者模式案例

下面以新闻订阅为例实现观察者模式:假设我们有一个新闻发布者(主题),它可以添加和删除订阅者(观察者),并在发布新闻时通知所有订阅者。订阅者可以是各种类型的用户,比如网站访问者、移动应用用户等。

设计类图如下

观察者模式-新闻发布系统.drawio

主题接口

public interface NewsPublisher {
    void registerSubscriber(NewsSubscriber subscriber);
    void removeSubscriber(NewsSubscriber subscriber);
    void notifySubscribers(String news);
}

观察者接口

public interface NewsSubscriber {
    void update(String news);
}

主题实现接口

public class NewspaperPublisher implements NewsPublisher {
    private List<NewsSubscriber> subscribers = new ArrayList<>();

    @Override
    public void registerSubscriber(NewsSubscriber subscriber) {
        subscribers.add(subscriber);
    }

    @Override
    public void removeSubscriber(NewsSubscriber subscriber) {
        subscribers.remove(subscriber);
    }

    @Override
    public void notifySubscribers(String news) {
        for (NewsSubscriber subscriber : subscribers) {
            subscriber.update(news);
        }
    }

    public void publishNews(String news) {
        notifySubscribers(news);
    }
}

观察者具体实现接口

//PC端
public class WebsiteSubscriber implements NewsSubscriber {
    private String name;

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

    @Override
    public void update(String news) {
        System.out.println("Website subscriber " + name + " received news: " + news);
    }
}

//移动端
public class MobileAppSubscriber implements NewsSubscriber {
    private String name;

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

    @Override
    public void update(String news) {
        System.out.println("Mobile app subscriber " + name + " received news: " + news);
    }
}

三、JDK内置的观察者模式实现

JDK已经有内置的观察者模式的实现方式。这个实现方式就是使用了 java.util.Observable 类和 java.util.Observer 接口。

java.util.Observable:可观察的主题类,该类不是抽象类,继承它可方便的集成主题类相关的功能:管理观察者以及通知观察者。

java.util.Observer:观察者接口类,和Observable类搭配使用,实现该接口可接收主题类发送的通知消息。

还是上面的案例,改成使用JDK内置的两个工具类

可观察的主题类

public class NewsPublisher extends Observable {
    private String news;

    public void publishNews(String news) {
        this.news = news;
        setChanged();
        notifyObservers(news);
    }
}

观察者类

public class NewsSubscriber implements Observer {
    private String name;

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

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(name + " received news: " + arg);
    }
}

测试类

public class JDKObserverPatternExample {
    public static void main(String[] args) {
        NewsPublisher publisher = new NewsPublisher();

        NewsSubscriber subscriber1 = new NewsSubscriber("Subscriber 1");
        NewsSubscriber subscriber2 = new NewsSubscriber("Subscriber 2");

        publisher.addObserver(subscriber1);
        publisher.addObserver(subscriber2);

        publisher.publishNews("Breaking news: A new product launched!");
    }
}

在上面的示例中,NewsPublisher 是一个可观察的主题类,它继承了 Observable 类。NewsSubscriber 是观察者类,它实现了 Observer 接口。在 JDKObserverPatternExample 类中,我们创建了一个 NewsPublisher 对象,并向其添加了两个观察者对象。当新闻发布者发布新闻时,所有的观察者都会收到通知并作出相应的响应。

四、Spring框架源码中的观察者模式

1、使用案例

Spring 框架中的观察者模式主要体现在事件驱动编程方面。Spring 通过事件驱动机制来实现模块之间的解耦,允许一个模块(发布者)在状态改变时通知其他模块(订阅者)。

首先先看下怎么用

定义事件类,需要继承ApplicationEvent

import org.springframework.context.ApplicationEvent;

public class CustomEvent extends ApplicationEvent {
    
	private static final long serialVersionUID = -1L;
	private String message;
    
	public CustomEvent(Object source, String message) {
		super(source);
		this.message = message;
	}
	public String getMessage() {
		return message;
	}
}

定义发布者

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class EventPublisher {

	private final AnnotationConfigApplicationContext context;

	public EventPublisher(AnnotationConfigApplicationContext context) {
		this.context = context;
	}

	public void publishCustomEvent(String messaga) {
		context.publishEvent(new CustomEvent(this, messaga));
	}
}

定义监听者

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
	@Override
	public void onApplicationEvent(CustomEvent event) {
		System.out.println("Received custom event: " + event.getMessage());
	}
}

启动类

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(CustomEventListener.class);
		context.register(EventPublisher.class);
		context.refresh();

		EventPublisher publisher = context.getBean(EventPublisher.class);
		publisher.publishCustomEvent("Hello,Spring Events !");

	}
}

运行结果

Received custom event: Hello,Spring Events !

如果我再添加一个监听者

@Component
public class CustomEventListener1 implements ApplicationListener<CustomEvent> {
	@Override
	public void onApplicationEvent(CustomEvent event) {
		System.out.println("Received custom event1: " + event.getMessage());
	}
}

那运行结果就变成了

Received custom event: Hello,Spring Events !
Received custom event1: Hello,Spring Events !

2、源码解析

入口在org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)方法,debug进入该方法,最终可以看到如下代码

image-20240407091129417

查找所有该类型Event的监听者,并for循环挨个执行invokeListener方法:

image-20240407091316636

实际上执行的就是onApplicationEvent方法,实现了监听者回调。



END.


#设计模式
目录