观察者模式
定义
又叫发布-订阅模式、模型-视图模式、源-监听器模式或从属者模式。定义一种一对多的依赖关系,一个主题对象可被多个观察者同时监听,使得每当主题对象的状态变化时,所有依赖于它的对象都回得到同事并自动更新。属于行为型模式
观察者模式的核心是将观察者与被观察者解耦,以类似于消息/广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。
适用情景
在软件系统中,当系统一方行为依赖于另一方行为的变动时,可以适用观察者模式松耦合联动双发,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。如闹钟,app角标通知,邮件通知,桌面程序的事件响应。
- 当一个抽象模型包含两个方面内容,其中一个方面依赖于另一个方面。
- 其他一个或多个对象的变化依赖与另一个对象的变化;
- 实现类似于广播机制的功能,无需知道具体的收听者,只需要分发广播,系统中感兴趣的对象会自动接受该广播
- 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知
角色
- 抽象主题(Subject):指被观察的对象。该角色是一个抽象类或接口,定义了增加,删除,通知观察者对象的方法。
- 具体主题(ConcreteSubject):具体被观察者,当其内状态发生变化时,会通知已注册的观察者。
- 抽象观察者(Observer):定义了响应通知的更新方法。
- 具体观察者(ConcreteObserver):在得到状态更新是,会自动做出响应。
实例
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;
}
}
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(GPer gPer, Question question) {
System.out.println(String.format("%s 在 %s 上提交了一个问题",question.getUserName(), this.name));
setChanged();
notifyObservers(question);
}
public class Teacher implements Observer {
private String name;
public Teacher(String name) {
this.name = name;
}
public void update(Observable observable, Object o) {
GPer gPer = (GPer) observable;
Question question = (Question) o;
System.out.println(String.format("%s 老师,你好!\n 您收到了一个来自 %s 的提问,内容如下:\n %s \n 提问者:%s",
name, gPer.getName(), question.getContent(), question.getUserName()));
}
}
public class Test {
public static void main(String[] args) {
GPer gPer = GPer.getInstance();
Teacher tom = new Teacher("Tom");
gPer.addObserver(tom);
Question question = new Question();
question.setUserName("小明");
question.setContent("观察者使用的场景");
gPer.publishQuestion(gPer,question);
}
}
观察者框架Guave
public class GuavaEvent {
public void subscribe(String str){
System.out.println(String.format("执行subscribe方法,传入的参数是:%s",str));
}
}
public class Test {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
GuavaEvent guavaEvent = new GuavaEvent();
eventBus.register(guavaEvent);
eventBus.post("Tom");
}
}
观察者模式在源码中的使用
Spring中的ContextLoaderListener实现了ServletContextListener接口,而ServletContextListener接口又继承了EventListener,在JDK中EventListener使用范围广泛。
优点
- 观察者与被观察者是松耦合(抽象耦合)的,符合依赖导致原则;
- 分离了表示层(观察者)和数据逻辑层(被观察者),并创建了一套触发机制,使得数据的变化可以响应到多个表示层上;
- 实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观测者触发事件时,只有感兴趣的观察者可以接收到通知。
缺点
- 如果观察者数量过多,则事件通知或耗时较长;
- 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接手该事件;
- 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,到时系统崩溃。