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

275 阅读3分钟

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

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

1.1 学生在线提问的案例

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

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

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

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);
    }

}

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

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());
    }
}

测试main方法:

 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);

}

测试运行结果:

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

2.1 Spring中的ContextLoaderListener

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

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());
    }
}

ServletContextListener :

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

    default void contextDestroyed(ServletContextEvent sce) {
    }
}

EventListener :

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

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

pom依赖

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>23.0</version>
</dependency>

创建监听事件GuavaEvent:

public class GuavaEvent {

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

}

测试main方法:

public static void main(String[] args) {

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

}

三、观察者模式的优缺点

优点:

  • 观察者和被观察者之间建立了一个抽象的耦合;

  • 观察者模式支持广播通信。

缺点:

  • 观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度;

  • 使用要得当,要避免循环调用。