字节面试:请用一个故事表示观察者模式!

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

什么是观察者模式?

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

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

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

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

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

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

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

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

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

应用实例

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

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

  • 主体接口

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

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

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

csharp 复制代码
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();
}
  • 实际观察者(小红、小蓝、小绿)

分别实现三个同学:

scala 复制代码
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 + "把小说藏起来!继续学习!");
    }
}
scala 复制代码
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 + "把手机收起来!继续学习!");
    }
}
scala 复制代码
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 + "把游戏机收起来!继续学习!");
    }
}
  • 实际发布者(小黄)

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

typescript 复制代码
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;
    }
}
  • 运行客户端
java 复制代码
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 设计模式》

《大话设计模式》

相关推荐
AlienZHOU2 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
野生技术架构师3 小时前
2026 Java 面试全套总结,八股 + 场景 + AI 相关面试考点
java·人工智能·面试
狗头大军之江苏分军4 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术5 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong5 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7045 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc5 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae6 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
计算机魔术师7 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen7 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程