三类设计模式:
- 创建型设计模式:主要解决“对象的创建”问题
- 结构型设计模式:主要解决“类或对象的组合或组装”问题
- 行为型设计模式:主要解决的就是“类或对象之间的交互”问题
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。
观察者模式主要用于在关联行为之间建立一套触发机制的场景。观察者模式在现实生活应用也非常广泛, 比如:微信朋友圈动态通知、邮件通知、广播通知、桌面程序的事件响应等,如下图
一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener。不管怎么称呼,只要应用场景符合刚刚给出的定义,都可以看作观察者模式。
1.代码示例
Subject
// 被观察者接口
// 只要被观察者(subject)发生改变,就要通知观察者(observer)
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(Message message);
}
// 具体的被观察者对象,一般唯一
public class ConcreteSubject implements Subject {
// 保存当前 Subject 的观察者们
private List<Observer> observers = new ArrayList<Observer>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
// 遍历观察者们(observer),调用他们的update方法发送变更消息
public void notifyObservers(Message message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
Observer
// 观察者接口
public interface Observer {
void update(Message message);
}
// 具体的观察者1,一般有多个
public class ConcreteObserverOne implements Observer {
@Override
public void update(Message message) {
//TODO: 获取消息通知,执行自己的逻辑...
System.out.println("ConcreteObserverOne is notified.");
}
}
// 观察者2
public class ConcreteObserverTwo implements Observer {
@Override
public void update(Message message) {
//TODO: 获取消息通知,执行自己的逻辑...
System.out.println("ConcreteObserverTwo is notified.");
}
}
测试代码
public class Demo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
// 给subject添加观察者们
subject.registerObserver(new ConcreteObserverOne());
subject.registerObserver(new ConcreteObserverTwo());
// subject调用notify方法 --> observer的update方法
subject.notifyObservers(new Message());
}
}
实际上,上面的代码算是观察者模式的“模板代码”,只能反映大体的设计思路。在真实的软件开发中,并不需要照搬上面的模板代码。观察者模式的实现方法各式各样,函数、类的命名等会根据业务场景的不同有很大的差别,比如 register 函数还可以叫作 attach,remove 函数还可以叫作 detach 等等。不过,万变不离其宗,设计思路都是差不多的。
另外,从刚刚的分类方式上来看,它是一种同步阻塞的实现方式。观察者和被观察者代码在同一个线程内执行,被观察者一直阻塞,直到所有的观察者代码都执行完成之后,才执行后续的代码。如果 Observer 的接口是一个调用比较频繁的接口,对性能非常敏感,希望接口的响应时间尽可能短,那我们可以将同步阻塞的实现方式改为异步非阻塞的实现方式(创建一个新线程去执行),以此来减少响应时间。
2.源码应用
来看一下 Spring 中的 ContextLoaderListener 实现了 ServletContextListener 接口, ServletContextListener 接口又继承了 EventListener,在 JDK 中 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 {
public void contextInitialized(ServletContextEvent sce);
public void contextDestroyed(ServletContextEvent sce);
}
EventListener:
package java.util;
public interface EventListener {
}
3.总结
设计模式要干的事情就是解耦,创建型模式是将创建和使用代码解耦,结构型模式是将不同功能代码解耦,行为型模式是将不同的行为代码解耦,具体到观察者模式,它将观察者和被观察者代码解耦。
借助设计模式,我们利用更好的代码结构,将一大坨代码拆分成职责更单一的小类,让其满足开闭原则、高内聚低耦合等特性,以此来控制和应对代码的复杂性,提高代码的可扩展性。
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅、RSS Feeds,本质上都是观察者模式。
不同的应用场景和需求下,这个模式也有截然不同的实现方式,有同步阻塞的实
现方式,也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。
观察者模式的优缺点
优点:
- 观察者和被观察者之间建立了一个抽象的耦合
- 观察者模式支持广播通信
缺点:
- 观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度
- 使用要得当,要避免循环调用
基于 Guava API 轻松落地观察者模式
在这里,再给大家推荐一个实现观察者模式非常好用的框架,API 使用也非常简单。
先引入 maven 依赖包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
创建侦听事件 GuavaEvent:
public class GuavaEvent {
@Subscribe
public void subscribe(String str){
System.out.println("执行subscribe方法,传入的参数是:" + str);
}
}
客户端测试代码:
public class GuavaEventTest {
public static void main(String[] args) {
// 消息总线
EventBus eventBus = new EventBus();
GuavaEvent guavaEvent = new GuavaEvent();
eventBus.register(guavaEvent);
eventBus.post("Tom");
// 从Struts到SpringMVC的升级
// 因为Struts面向的类,而SpringMVC面向的是方法
// 前面两者面向的是类,Guava面向是方法
// 能够轻松落地观察模式的一种解决方案
// MQ
}
}






还没有评论,来说两句吧...