观察者模式详解
观察者模式
1.1观察者模式概述
观察者模式(Observer Pattern)又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式。定义了一种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖它的对象都会得到通知并自动更新,属于行为型设计模式。
观察者的核心是将观察者与被观察者解耦,以类似消息/广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。
1.2观察者模式的应用场景
观察者模式一般会应用到App的闹钟设置,消息的广播通知,邮件通知等。
1.3观察者模式的通用写法
/**
* 抽象主题者
*
* @author yml
* @since 2022/4/13 11:24
*/
public interface ISubject<T> {
boolean attach(IObserver<T> observer);
boolean detach(IObserver<T> observer);
void notify(T event);
}
/**
* 抽象观察者
*
* @author yml
* @since 2022/4/13 11:21
*/
public interface IObserver<T> {
void update(T event);
}
/**
* 具体主题者
*
* @author yml
* @since 2022/4/13 14:46
*/
public class ConcreteSubject<T> implements ISubject<T> {
private List<IObserver<T>> observers = new ArrayList<IObserver<T>>();
@Override
public boolean attach(IObserver<T> observer) {
return !this.observers.contains(observer) && this.observers.add(observer);
}
@Override
public boolean detach(IObserver<T> observer) {
return this.observers.remove(observer);
}
@Override
public void notify(T event) {
for (IObserver<T> observer:this.observers) {
observer.update(event);
}
}
}
/**
* 具体观察者
*
* @author yml
* @since 2022/4/13 14:43
*/
public class ConcreteObserver<T> implements IObserver<T> {
@Override
public void update(T event) {
System.out.println("receive event: " + event);
}
}
/**
* @Author:yml
* @Data:2022/4/13
*/
public class ClientTest {
public static void main(String[] args) {
// 被观察者
ISubject<String> subject = new ConcreteSubject<>();
// 观察者
IObserver<String> observer = new ConcreteObserver<>();
// 将观察者注册
subject.attach(observer);
// 被观察者通知观察者
subject.notify("hello");
}
}
绘制出观察者uml图:
由上图可以看出,观察者模式主要包含4个角色:
- 抽象主题(ISubject):指被观察的对象。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法。
- 具体主题(ConcreteSubject):具体被观察者,当其内部状态发生变化时,会通知已注册的观察者。
- 抽象观察者(IObserver):定义了响应通知的更新方法。
- 具体观察者(ConcreteObserver):当得到状态更新通知时,会自动做出响应。
2.1观察者模式的具体实现
2.1.1 JDK中的观察者模式
模拟一个场景,在线教育平台,当某个学生在线上提出问题并指出对应指导老师回答,指定老师则会收到相应邮件通知
import java.util.Observable;
/**
* 被观察者
*
* @author yml
* @since 2022/4/13 15:48
*/
public class GPer extends Observable {
private String name = "GPer 平台";
private static GPer gPer = null;
private GPer(){}
public static GPer newInstance(){
if (null == gPer){
gPer = new GPer();
}
return gPer;
}
public String getName() {
return name;
}
public void publishQuestion(Question question){
System.out.println(question.getUserName()+":"+question.getContent());
setChanged();
notifyObservers(question);
}
}
/**
* @Author:yml
* @Data:2022/4/13
*/
public class Question {
private String userName;
private String content;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
import java.util.Observable;
import java.util.Observer;
/**
* @Author:yml
* @Data:2022/4/13
*/
public class Teacher implements Observer {
private String name;
public Teacher(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
GPer gPer = (GPer) o;
Question question = (Question) arg;
System.out.println("===========================================");
System.out.println(this.name + "老师,你好 \n" + "您收到一条来自" + gPer.getName() + "\n" + "提问者:" + question.getUserName() +
"\n" + "问题描述:" + question.getContent());
}
}
/**
* @Author:yml
* @Data:2022/4/13
*/
public class ClientTest {
public static void main(String[] args) {
GPer gPer = GPer.newInstance();
Teacher tom = new Teacher("Tom");
Teacher jerry = new Teacher("Jerry");
gPer.addObserver(tom);
gPer.addObserver(jerry);
// 用户操作
Question question = new Question();
question.setUserName("amazing");
question.setContent("如何学好JAVA");
gPer.publishQuestion(question);
}
}
2.1.2 基于Guava API实现观察者模式
需要引入maven依赖包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
import com.google.common.eventbus.EventBus;
import org.example.observereg.Question;
/**
* @Author:yml
* @Data:2022/4/13
*/
public class GuavaBus {
public static final EventBus bus = new EventBus();
public static void register(GuavaEvent event){
if (event == null){
return;
}
bus.register(event);
}
public static void question(Question question){
bus.post(question);
}
}
import com.google.common.eventbus.Subscribe;
import org.example.observereg.Question;
/**
* @Author:yml
* @Data:2022/4/13
*/
public class GuavaEvent {
@Subscribe
public void subscribe(Question question){
System.out.println(question.getUserName()+":"+question.getContent());
}
}
import org.example.observereg.Question;
/**
* @Author:yml
* @Data:2022/4/13
*/
public class GuavaEventTest {
public static void main(String[] args) {
GuavaEvent guavaEvent = new GuavaEvent();
GuavaBus.register(guavaEvent);
Question question = new Question();
question.setUserName("amazing");
question.setContent("如何学好JAVA");
GuavaBus.question(question);
}
}
3.1 观察这模式在Spring源码中的应用
Spring中的事件机制,Spring中的ContextLoaderListener实现ServletContextListener,ServletContextListener又继承JDK的EventListener,实现事件监听。
3.2 观察者模式的优缺点
- 优点:
1.观察者和被观察者都是松耦合(抽象耦合),符合依赖倒置原则。
2.分离了表示层(观察者)和数据逻辑层(被观察者者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层。
3.实现了一对多的通信机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发时间是,只有订阅的观察者可以接受通知。 - 缺点
1.如果观察者过多,则事件通知会耗时较长。
2.事件通知为线性,可能出现阻塞。
3.观察者和被观察者可能存在循环依赖,可能造成循环调用,导致系统崩溃。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?