观察者模式
所有源代码地址 https://gitee.com/zyxscuec/Design-pattern.git
文章目录
(1) 概念
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。
(2)适用场景
观察者模式主要用于在关联行为之间建立一套触发机制的场景。观察者模式在现实生活应用也非常广泛,
比如:微信朋友圈动态通知、GPser 生态圈消息通知、邮件通知、广播通知、桌面程序的事件响应等(如下图)。
如果有设置指定老师回答,对应的老师就会收到邮件通知,这就是观察者模式的一种应用场
景。我们可能会想到 MQ,异步队列等,其实 JDK 本身就提供这样的 API。
(3) 代码示例
我们用代码来还原一下这样一个应用场景,创建 GPer 类:
package com.alibaba.design.observerpattern.gperadvice; import java.util.Observable; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:42 */ public class GPer extends Observable { private String name = "GPer生态圈"; private static GPer gper = null; private GPer(){} public static GPer getInstance(){ if(null == gper){ gper = new GPer(); } return gper; } public String getName() { return name; } public void publishQuestion(Question question){ System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。"); setChanged(); notifyObservers(question); } }
创建问题 Question 类:
package com.alibaba.design.observerpattern.gperadvice; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:43 */ 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; } }
创建老师 Teacher 类:
package com.alibaba.design.observerpattern.gperadvice; import java.util.Observable; import java.util.Observer; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:43 */ 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(name + "老师,你好!\n" + "您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:\n" + question.getContent() + "\n" + "提问者:" + question.getUserName()); } }
客户端测试代码
package com.alibaba.design.observerpattern.gperadvice; import java.util.Observer; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:44 */ public class ObserverTest { public static void main(String[] args) { GPer gper = GPer.getInstance(); Teacher tom = new Teacher("Tom"); Teacher mic = new Teacher("Jerry"); //这为没有@Tom老师 Question question = new Question(); question.setUserName("小明"); question.setContent("观察者设计模式适用于哪些场景?"); gper.addObserver(tom); gper.addObserver(mic); gper.publishQuestion(question); } }
(4) 模式在源码中的体现
来看一下 Spring 中的 ContextLoaderListener 实现了 ServletContextListener 接口,ServletContextListener 接口又继承了 EventListener,在 JDK 中 EventListener 有非常广泛的应用。我们可以看一下源代码,ContextLoaderListener:
package org.springframework.web.context; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @override public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } @override public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
ServletContextListener
package javax.servlet; import java.util.EventListener; public interface ServletContextListener extends EventListener { public void contextInitialized(ServletContextEvent sce); public void contextDestroyed(ServletContextEvent sce); }
EventListener
package java.util; public interface EventListener { }
(5)基于 Guava API 轻松落地观察者模式
Guava是一种基于开源的Java库,Google Guava源于2007年的"Google Collections Library"。这个库是为了方便编码,并减少编码错误。这个库用于提供集合,缓存,支持原语句,并发性,常见注解,字符串处理,I/O和验证的实用方法。
在这里,我还推荐给大家一个实现观察者模式非常好用的框架。API 使用也非常简单,举
个例子,先引入jar包:guava-20.jar
这里建议去maven下载这个
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>20.0</version> </dependency>
创建侦听事件 GuavaEvent:
package com.alibaba.design.observerpattern.guava; import com.google.common.eventbus.Subscribe; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:59 */ public class GuavaEvent { @Subscribe public void subscribe(String str){ System.out.println("执行subscribe方法,传入的参数是:" + str); } }
客户端测试代码:
package com.alibaba.design.observerpattern.guava; import com.google.common.eventbus.EventBus; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-9:59 */ public class GuavaEventTest { public static void main(String[] args) { //消息总线 EventBus eventBus = new EventBus(); GuavaEvent guavaEvent = new GuavaEvent(); eventBus.register(guavaEvent); eventBus.post("Tom"); } }
(6) 观察者模式的优缺点
- 优点:
1、观察者和被观察者之间建立了一个抽象的耦合。
2、观察者模式支持广播通信。 - 缺点:
1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。
2、使用要得当,要避免循环调用。