从一个小故事讲解观察者模式~

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

什么是观察者模式?

观察者模式在我们的日常生活中极其常见。

先来看看观察者模式的定义:

观察者模式定义了对象之间一对多的依赖,当这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新

这个模式在我们日常生活中可以说是太常见了!

比如手机上的天气预报,一旦气象局的信息进行了更新,手机上的天气信息就会相应的变化;

比如10086群发的消息,其实也是一个观察者模式的例子,每当10086发送一条消息时,所有的移动手机都会收到这个消息。而且一般消息中都会附带一个提醒"退订请回复T",一旦回复了T,以后就不会接收到显10086对应的短信了

比如麦当劳吃炸鸡时,当一份食物准备好,就会通知"3045、3045、3045"请取餐,大家都能听到,但是可能只有一个人会去取餐,因为订阅者可以自行决定接收到消息之后动不动,或者说做什么;又比如...

观察者模式描述的就是订阅者和发布者之间的联系。其UML类图如下:

在这个下面给出订阅者和发布者的一个例子。

应用实例

借鉴《大话设计模式》的看门放哨的例子,我们来设计这样一个场景:

"一堂自习课时,小红、小蓝、小绿 三人无心学习,各玩各的。坐在门口小黄 在门口放哨,一旦看到班主任的身影,就敲一下桌子,其他三人应声而动,赶忙拿出书进行学习。当班主任走远了,小黄敲两下桌子,大家又开始愉快的玩耍..."

  • 主体接口

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

复制代码
public interface Subject {
    // 增加观察者
    public void attach(Observer observer);
    // 删除观察者
    public void detach(Observer observer);
    // 通知所有观察者
    public void notifyObservers();
    // 主体(被观察者)状态
    public void setAction(String action);
    public String getAction();
}
  • 通知者接口

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

复制代码
public abstract class Observer {
    protected String name;
    protected Subject subject;
    public Observer(String name, Subject subject) {
        this.name = name;
        this.subject = subject;
    }
    public abstract void update();
}
  • 实际观察者(小红、小蓝、小绿)

分别实现三个同学:

复制代码
public class Xiaohong extends Observer {
    public Xiaohong(String name, Subject subject) {
        super(name, subject);
    }
    @Override
    public void update() {
        System.out.println(subject.getAction() + "\n" + name + "把小说藏起来!继续学习!");
    }
}

public class Xiaolan extends Observer {
    public Xiaolan(String name, Subject subject) {
        super(name, subject);
    }
    @Override
    public void update() {
        System.out.println(subject.getAction() + "\n" + name + "把手机收起来!继续学习!");
    }
}

public class Xiaolv extends Observer {
    public Xiaolv(String name, Subject subject) {
        super(name, subject);
    }
    @Override
    public void update() {
        System.out.println(subject.getAction() + "\n" + name + "把游戏机收起来!继续学习!");
    }
}
  • 实际发布者(小黄)

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

复制代码
public class Xiaohuang implements Subject {
    // 需要通知的同学列表
    private final List<Observer> observers = new LinkedList<>();
    private String action;
    // 添加通知同学
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }
    // 删除通知同学
    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    // 通知所有同学
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
    // 获取通知状态
    @Override
    public String getAction() {
        return action;
    }
    // 设置通知状态
    @Override
    public void setAction(String action) {
        this.action = action;
    }
}
  • 运行客户端

    public class App {
    public static void main(String[] args) {
    // 前台为通知者
    Xiaohuang xiaohuang = new Xiaohuang();
    Xiaohong observer1 = new Xiaohong("小红", xiaohuang);
    Xiaolv observer2 = new Xiaolv("小绿", xiaohuang);
    Xiaolan observer3 = new Xiaolan("小蓝", xiaohuang);
    // 需要通知三个
    xiaohuang.attach(observer1);
    xiaohuang.attach(observer2);
    xiaohuang.attach(observer3);
    // 设置通知状态
    xiaohuang.setAction("小心!Boss回来了!");
    // 发送通知
    xiaohuang.notifyObservers();
    }
    }

结果如下:

总结

优点

  • 观察者模式实现了发布者和订阅者之间的松耦合:两个对象之间可以进行交互,但是不太清楚彼此之间的细节;
  • 适用于一个对象改变,相联系的对象随之发生相应变化的场景

缺点

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

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

参考资料

《Head First 设计模式》

《大话设计模式》

观察者模式------极客教程

相关推荐
考虑考虑19 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613519 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊20 小时前
Java学习第22天 - 云原生与容器化
java
渣哥1 天前
原来 Java 里线程安全集合有这么多种
java
间彧1 天前
Spring Boot集成Spring Security完整指南
java
间彧1 天前
Spring Secutiy基本原理及工作流程
java
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
Java水解1 天前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试