1.观察者模式Observer Mode
观察者模式,又叫发布-订阅(Publish/Subscribe)模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式或从属者(Dependents)模式。定义一种一对多的依赖关系,一个主题对象可被多个观察者同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被自动更新。
1.1 代码实现
通过一个微信用户(观察者)订阅公众号(被观察者)接收公众号推送消息的例子来进行简单的代码实现:
// 抽象观察者接口
public interface Observer {
void update(String message);
}
// 微信用户类 具体的观察者
@AllArgsConstructor
public class WeixinUser implements Observer {
private String name;
@Override
public void update(String message) {
System.out.println(name + "接收到了消息(观察到了):" + message);
}
}
// 被观察者接口
public interface Observable {
// 新增用户(新增观察者)
void add(Observer observer);
// 移除用户,或者说用户取消订阅(移除观察者)
void del(Observer observer);
// 发布 推送消息
void notify(String message);
}
// 具体的被观察者(公众号)
public class Subject implements Observable {
// 观察者列表(订阅用户)
private List<Observer> list = new ArrayList<>();
@Override
public void add(Observer observer) {
list.add(observer);
}
@Override
public void del(Observer observer) {
list.remove(observer);
}
// 给每一个观察者(订阅者)推送消息
@Override
public void notify(String message) {
list.forEach(observer -> observer.update(message));
}
}
// 测试
public static void main(String[] args){
Observable o = new Subject();
WeixinUser user1 = new WeixinUser("张三");
WeixinUser user2 = new WeixinUser("李四");
WeixinUser user3 = new WeixinUser("王五");
o.add(user1);
o.add(user2);
o.add(user3);
o.notify("薛之谦演唱会要来到广州啦!");
// 运行结果
// 张三接收到了消息(观察到了):薛之谦演唱会要来到广州啦!
// 李四接收到了消息(观察到了):薛之谦演唱会要来到广州啦!
// 王五接收到了消息(观察到了):薛之谦演唱会要来到广州啦!
}
1.2 JDK实现
在 Java 中,通过java.util.Observable类和 java.util.Observer接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
1.2.1 Observable类
Observable类是抽象目标类(被观察者),它有一个Vector集合成员变量,用于保存所有要通知的观察者对象,下面是它最重要的 3 个方法:
void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
void setChange() 方法:用来设置一个boolean类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
1.2.2 Observer 接口
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。
1.2.3 代码实现
下面还是通过微信用户订阅公众号的例子进行代码实现,方便对比他们之间的区别:
// 具体的被观察者(公众号)
@Data
@AllArgsConstructor
public class Subject extends Observable {
// 公众号的名字
private String name;
// 公众号发布消息
public void notifyMessage(String message) {
System.out.println(this.name + "公众号发布消息:" + message + "请关注用户留意接收!");
super.setChanged();
super.notifyObservers(message);
}
}
@AllArgsConstructor
public class WeixinUser implements Observer {
private String name;
/**
* @param o 被观察者
* @param arg 被观察者带过来的参数,此例子中是公众号发布的消息
*/
@Override
public void update(Observable o, Object arg) {
System.out.println(name + "关注了公众号(被观察者):" + ((Subject)o).getName() + ",接收到消息:" + arg);
}
}
// 测试
public static void main(String[] args){
WeixinUser user1 = new WeixinUser("张三");
WeixinUser user2 = new WeixinUser("李四");
WeixinUser user3 = new WeixinUser("王五");
Subject subject = new Subject("演唱会消息发布");
subject.addObserver(user1);
subject.addObserver(user2);
subject.addObserver(user3);
subject.notifyMessage("薛之谦演唱会要来到广州啦!");
// 返回结果
// 演唱会消息发布公众号发布消息:薛之谦演唱会要来到广州啦!请关注用户留意接收!
// 王五关注了公众号(被观察者):演唱会消息发布,接收到消息:薛之谦演唱会要来到广州啦!
// 李四关注了公众号(被观察者):演唱会消息发布,接收到消息:薛之谦演唱会要来到广州啦!
// 张三关注了公众号(被观察者):演唱会消息发布,接收到消息:薛之谦演唱会要来到广州啦!
}
1.3 Google的Guava实现
@AllArgsConstructor
public class WeixinUser {
private String name;
@Subscribe
public void getMessage(Object arg) {
System.out.println(this.name + "接收到消息:" + arg);
}
// 测试
public static void main(String[] args){
// 消息总线
EventBus eventBus = new EventBus();
eventBus.register(new WeixinUser("张三"));
eventBus.register(new WeixinUser("李四"));
eventBus.post("薛之谦演唱会要来到广州啦!");
// 返回结果
// 张三接收到消息:薛之谦演唱会要来到广州啦!
// 李四接收到消息:薛之谦演唱会要来到广州啦!
}
}
1.4 总结
适用场景:
- 当一个抽象模型包含两个方面内容,其中一个方面依赖于另一个方面。
- 其他一个或多个对象的变化依赖于另一个对象的变化。
- 实现类似广播机制的功能,无需知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。
优点:
- 观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则。
- 分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以相应到多个表示层上。
- 实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。
缺点:
- 如果观察者数量过多,则事件通知会耗时较长。
- 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件。
- 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。