架构师内功心法,先发布后订阅的观察者模式详解

一、观察者模式的应用场景

生活中的微信朋友圈动态通知、工作中的邮件通知、学校操场上的广播通知、电脑桌面应用程序的事件响应等等,这些都是观察者模式的现实生活场景。观察者模式(Observer Pattern)定义了对象之间一对多的依赖,让多个观察者监听一个主体对象,当主体对象发生变化时,它的所有观察者都会收到通知并且更新。

观察者模式也叫发布订阅模式。

架构师内功心法,先发布后订阅的观察者模式详解

1.1 学生在线提问的案例

现在还是新冠状病毒疫情期间,很多学生在家里学习遇到问题,难免需要请教自己的老师,当学生在某教育平台上提问的时候,如果有设置指定的老师进行答复,那么对应的老师会受到相应的邮件通知,这就是观察者模式的一种应用场景。拿到这个需要的时候我们可能会想到异步队列、消息中间件MQ等一些技术手段,其实JDK本身就提供了这样的API,那么我们来写关于这个场景的示例代码,创建 Question 问题类:

<code>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;
}
}
/<code>

再创建教育平台被观察者 Education 类继承 Observable :

<code>public class Education extends Observable {

private final String EDU_NAME = "xxx互联网教育平台";

private Education() {}

private static Education education = null;

public static Education getInstance() {
if(null == education) {
education = new Education();
}
return education;
}

public void publishQuestion(Question question) {
System.out.println(question.getUserName() + "在" + EDU_NAME + "上提交了一个问题!");
this.setChanged();
this.notifyObservers(question);
}

}
/<code>

创建老师 Teacher 类实现 Observer接口:

<code>public class Teacher implements Observer {

private String name;

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

@Override
public void update(Observable o, Object arg) {
Education education = (Education) o;
Question question = (Question) arg;
System.out.println("===============================");
System.out.println(name + "老师,你好!\\n" +
"您收到了一个来自“" + education.getEduName() + "”的提问,希望您解答,问题内容如下:\\n" +
question.getContent() + "\\n" +
"提问者:" + question.getUserName());
}

}
/<code>

测试main方法:

<code> public static void main(String[] args) {

Education edu = Education.getInstance();
Teacher teacher1 = new Teacher("Kevin");
Teacher teacher2 = new Teacher("Jhon");

edu.addObserver(teacher1);
edu.addObserver(teacher2);

Question question = new Question();
question.setUserName("小芳");
question.setContent("观察者模式有哪些应用场景?");

edu.publishQuestion(question);

}
/<code>

测试运行结果:

架构师内功心法,先发布后订阅的观察者模式详解

二、观察者模式在源码中的体现

2.1 Spring中的ContextLoaderListener

Spring中的ContextLoaderListener实现了ServletContextListener接口,ServletContextListener又继承了EventListener,我们来看下ContextLoaderListener源码:

<code>public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}

public ContextLoaderListener(WebApplicationContext context) {
super(context);
}

public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}

public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
/<code>

ServletContextListener :

<code>public interface ServletContextListener extends EventListener {
default void contextInitialized(ServletContextEvent sce) {
}

default void contextDestroyed(ServletContextEvent sce) {
}
}
/<code>

EventListener :

<code>/** 

* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
/<code>

2.2 基于Guava API实现观察者模式

pom依赖

<code><dependency>
\t<groupid>com.google.guava/<groupid>
\t<artifactid>guava/<artifactid>
\t<version>23.0/<version>
/<dependency>
/<code>

创建监听事件GuavaEvent:

<code>public class GuavaEvent {

@Subscribe
public void subscrible(String str) {
System.out.println("执行subscrible方法,传入的参数是:" + str);
}

}
/<code>

测试main方法:

<code>public static void main(String[] args) {

EventBus eventBus = new EventBus();
GuavaEvent guavaEvent = new GuavaEvent();
eventBus.register(guavaEvent);
eventBus.post("Kevin");

}
/<code>

三、观察者模式的优缺点

优点:

  • 观察者和被观察者之间建立了一个抽象的耦合;
  • 观察者模式支持广播通信。

缺点:

  • 观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度;
  • 使用要得当,要避免循环调用。


分享到:


相關文章: