定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

什么是观察者模式?

观察者模式在我们的日常生活中极其常见。
先来看看观察者模式的定义:
观察者模式定义了对象之间一对多的依赖,当这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新
这个模式在我们日常生活中可以说是太常见了!
比如手机上的天气预报,一旦气象局的信息进行了更新,手机上的天气信息就会相应的变化;
比如10086群发的消息,其实也是一个观察者模式的例子,每当10086发送一条消息时,所有的移动手机都会收到这个消息。而且一般消息中都会附带一个提醒“退订请回复T”,一旦回复了T,以后就不会接收到显10086对应的短信了
比如麦当劳吃炸鸡时,当一份食物准备好,就会通知“3045、3045、3045”请取餐,大家都能听到,但是可能只有一个人会去取餐,因为订阅者可以自行决定接收到消息之后动不动,或者说做什么;又比如…
观察者模式描述的就是订阅者和发布者之间的联系。其UML类图如下:
风紧扯呼——观察者模式 - 图1
在这个下面给出订阅者和发布者的一个例子。

应用实例

借鉴《大话设计模式》的看门放哨的例子,我们来设计这样一个场景:
“一堂自习课时,小红、小蓝、小绿三人无心学习,各玩各的。坐在门口小黄在门口放哨,一旦看到班主任的身影,就敲一下桌子,其他三人应声而动,赶忙拿出书进行学习。当班主任走远了,小黄敲两下桌子,大家又开始愉快的玩耍……”

  • 主体接口

主体接口,又称为被观察者,采用接口的方式可以给具体的主体类实现自己的逻辑,如采用链表、数组、或者Map等结构存储观察者。

  1. public interface Subject {
  2. // 增加观察者
  3. public void attach(Observer observer);
  4. // 删除观察者
  5. public void detach(Observer observer);
  6. // 通知所有观察者
  7. public void notifyObservers();
  8. // 主体(被观察者)状态
  9. public void setAction(String action);
  10. public String getAction();
  11. }
  • 通知者接口

通知者接口主要是定义了一个接口,不同的通知者实现自己的消息逻辑:

  1. public abstract class Observer {
  2. protected String name;
  3. protected Subject subject;
  4. public Observer(String name, Subject subject) {
  5. this.name = name;
  6. this.subject = subject;
  7. }
  8. public abstract void update();
  9. }
  • 实际观察者(小红、小蓝、小绿)

分别实现三个同学:

  1. public class Xiaohong extends Observer {
  2. public Xiaohong(String name, Subject subject) {
  3. super(name, subject);
  4. }
  5. @Override
  6. public void update() {
  7. System.out.println(subject.getAction() + "\n" + name + "把小说藏起来!继续学习!");
  8. }
  9. }
  1. public class Xiaolan extends Observer {
  2. public Xiaolan(String name, Subject subject) {
  3. super(name, subject);
  4. }
  5. @Override
  6. public void update() {
  7. System.out.println(subject.getAction() + "\n" + name + "把手机收起来!继续学习!");
  8. }
  9. }
  1. public class Xiaolv extends Observer {
  2. public Xiaolv(String name, Subject subject) {
  3. super(name, subject);
  4. }
  5. @Override
  6. public void update() {
  7. System.out.println(subject.getAction() + "\n" + name + "把游戏机收起来!继续学习!");
  8. }
  9. }
  • 实际发布者(小黄)

小黄观察老师的到来,通知大家:

  1. public class Xiaohuang implements Subject {
  2. // 需要通知的同学列表
  3. private final List<Observer> observers = new LinkedList<>();
  4. private String action;
  5. // 添加通知同学
  6. @Override
  7. public void attach(Observer observer) {
  8. observers.add(observer);
  9. }
  10. // 删除通知同学
  11. @Override
  12. public void detach(Observer observer) {
  13. observers.remove(observer);
  14. }
  15. // 通知所有同学
  16. @Override
  17. public void notifyObservers() {
  18. for (Observer observer : observers) {
  19. observer.update();
  20. }
  21. }
  22. // 获取通知状态
  23. @Override
  24. public String getAction() {
  25. return action;
  26. }
  27. // 设置通知状态
  28. @Override
  29. public void setAction(String action) {
  30. this.action = action;
  31. }
  32. }
  • 运行客户端

    1. public class App {
    2. public static void main(String[] args) {
    3. // 前台为通知者
    4. Xiaohuang xiaohuang = new Xiaohuang();
    5. Xiaohong observer1 = new Xiaohong("小红", xiaohuang);
    6. Xiaolv observer2 = new Xiaolv("小绿", xiaohuang);
    7. Xiaolan observer3 = new Xiaolan("小蓝", xiaohuang);
    8. // 需要通知三个
    9. xiaohuang.attach(observer1);
    10. xiaohuang.attach(observer2);
    11. xiaohuang.attach(observer3);
    12. // 设置通知状态
    13. xiaohuang.setAction("小心!Boss回来了!");
    14. // 发送通知
    15. xiaohuang.notifyObservers();
    16. }
    17. }

    结果如下:
    风紧扯呼——观察者模式 - 图2

    总结

    优点

  • 观察者模式实现了发布者和订阅者之间的松耦合:两个对象之间可以进行交互,但是不太清楚彼此之间的细节;

  • 适用于一个对象改变,相联系的对象随之发生相应变化的场景

    缺点

  • 如果对象之间有循环依赖,在观察者模式下可能会引发循环调用,从而造成系统崩溃;

总而言之,当对象之间的关系类似与消息群发这样的一对多关系时,就可以开始考虑用不用观察者模式了。(都这个时候了,不用它还能用谁啊?)

参考资料

《Head First 设计模式》
《大话设计模式》
观察者模式——极客教程
https://geek-docs.com/design-pattern/observer-pattern/observer-pattern-index.html#i-7截屏2021-08-12 下午2.08.39.png