卡码笔记
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
  • 基础与面向对象

  • 集合

  • 异常

  • 字符串

  • JVM

  • 并发与多线程

  • JDK

  • Spring

  • 设计模式

    • 单例模式
    • 工厂模式
    • 建造者模式
    • 代理模式
    • 策略模式
    • 装饰器模式
    • 观察者模式
      • 简要回答
      • 详细回答
      • 知识图解
      • 代码示例
      • 使用场景
      • 知识扩展

# 观察者模式

# 简要回答

  • 观察者模式是一种行为型设计模式,核心思想是定义一对多的对象依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态变化时会通知所有的观察者对象,触发响应行为;观察者能够动态订阅/取消订阅主题,实现了发布者与观察者的解耦。
  • 观察者模式优点:除了能够动态扩展观察者外,还能实现观察者对象和主题对象之间的解耦,让耦合的双方都依赖于抽象,不会相互影响;同时封装了主题状态变更的通知逻辑,不用手动遍历调用观察者。
  • 但是观察者模式的通知顺序是无序的,还有内存泄漏风险,若观察者订阅后没有及时取消订阅,会导致观察者对象无法被GC回收;如果观察者更新时触发主题再次变更,可能会引发循环通知而导致程序卡死。

# 详细回答

  • 观察者模式有四个核心角色:抽象主题、具体主题、抽象观察者、具体观察者。
    1. 抽象主题(Subject):定义主题的核心行为规范,提供了添加/删除观察者的方法和统一通知所有观察者的方法。
    2. 具体主题(ConcreteSubject):继承抽象主题,是被观察的对象,维护自身的业务状态,存储所有已订阅的观察者对象集合;在自身状态发生变化时主动调用通知方法,通知所有已订阅的观察者对象。
    3. 抽象观察者(Observer):定义观察者的核心行为规范,声明响应主题状态变化的更新方法。
    4. 具体观察者(ConcreteObserver):是订阅消息的对象,实现抽象观察者所声明的更新方法,在自身状态发生变化时,调用该方法,将自身状态作为参数传递给观察者。
  • 实现观察者模式时,需要先定义主题接口,包括注册、移除和通知观察者的方法;然后定义观察者接口,包括更新方法;再定义具体的主题类,维护一组观察者对象,实现在主题状态改变时,调用观察者的更新方法;最后定义具体的观察者类,实现更新方法。客户端负责创建主题对象和观察者对象,将观察者注册到主题中,实现主题改变状态时观察者得到通知并进行更新。

# 知识图解

  1. 观察者模式结构图 image

# 代码示例

  1. 以Java代码为例,实现一个简单的观察者模式如下:

    import java.util.ArrayList;
    import java.util.List;
    
    // 观察者接口
    interface Observer {
        void update(String message);
    }
    
    // 具体观察者
    class ConcreteObserver implements Observer {
        private String name;
    
        public ConcreteObserver(String name) {
            this.name = name;
        }
    
        @Override
        public void update(String message) {
            System.out.println("观察者 [" + name + "] 收到通知: " + message);
        }
    }
    
    // 被观察者接口(主题)
    interface Subject {
        void attach(Observer observer);   // 添加观察者
        void detach(Observer observer);   // 移除观察者
        void notifyObservers(String message); // 通知所有观察者
    }
    
    // 具体被观察者(具体主题)
    class ConcreteSubject implements Subject {
        private List<Observer> observers = new ArrayList<>();
    
        @Override
        public void attach(Observer observer) {
            if (observer == null) {
                throw new IllegalArgumentException("观察者不能为 null");
            }
            if (!observers.contains(observer)) {
                observers.add(observer);
            }
        }
    
        @Override
        public void detach(Observer observer) {
            observers.remove(observer);
        }
    
        @Override
        public void notifyObservers(String message) {
            if (message == null) {
                message = "";
            }
            for (Observer observer : observers) {
                observer.update(message);
            }
        }
    }
    
    // 测试类
    public class ObserverPatternDemo {
        public static void main(String[] args) {
            // 创建被观察者
            ConcreteSubject subject = new ConcreteSubject();
    
            // 创建观察者
            Observer obs1 = new ConcreteObserver("张三");
            Observer obs2 = new ConcreteObserver("李四");
            Observer obs3 = new ConcreteObserver("王五");
    
            // 注册观察者
            subject.attach(obs1);
            subject.attach(obs2);
            subject.attach(obs3);
    
            // 发送通知
            subject.notifyObservers("第一次更新:系统即将维护");
    
            // 移除一个观察者
            subject.detach(obs2);
    
            // 再次发送通知
            subject.notifyObservers("第二次更新:维护完成");
        }
    }
    
    /* 代码执行结果:
    观察者 [张三] 收到通知: 第一次更新:系统即将维护
    观察者 [李四] 收到通知: 第一次更新:系统即将维护
    观察者 [王五] 收到通知: 第一次更新:系统即将维护
    观察者 [张三] 收到通知: 第二次更新:维护完成
    观察者 [王五] 收到通知: 第二次更新:维护完成
    */
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
  2. Java中内置了观察者模式的核心API,核心类是java.util.Observable和java.util.Observer。Observable类是被观察者(抽象主题),内置了观察者集合和增删/通知方法;Observer类是观察者(抽象观察者),定义了更新方法。

    • 使用JDK原生API时,需要自定义主题类和观察者类并分别继承Observable和Observer类,当被观察者的状态发生改变后,需要调用setChanged()标记状态变更,然后调用notifyObservers()通知所有观察者。

# 使用场景

  • 观察者模式适用于当一个对象状态发生改变,需要通知多个其他对象进行更新的场景,尤其是不知道具体有多少个观察者对象的情况下,希望这些对象不是紧耦合的情况。
  1. 事件处理场景中,当进行GUI界面开发时需要使用观察者模式监听鼠标点击、键盘输入等事件;
  2. 实现发布-订阅模型时,使用观察者模式实现有一个主题负责发送通知,多个观察者负责监听并响应通知,如微服务中的事件驱动架构或消息队列系统场景;
  3. 观察者模式还可以用于实现通知业务,如短信推送、邮件通知等场景。
  4. 框架中也经常使用观察者模式,如Spring框架中的ApplicationContext事件监听机制,其中ApplicationListener接口定义了监听器(观察者),ApplicationEvent接口定义事件(主题);Redis框架中的Pub/Sub机制,也是基于观察者模式实现的。

# 知识扩展

  1. 面试官可能追问:
  • Q1:观察者模式和发布-订阅模式有什么区别?
    • 观察者模式没有中间层,主题直接持有观察者的引用,可以直接实现通知和调用其更新方法;适合单机且轻量级的场景。
    • 发布-订阅模式引入了中间层实现完全的解耦,发布者负责发布消息,由中间层负责将消息分发给订阅者,发布者和订阅者互相不知道对方的存在。适合分布式、跨进程的大型系统,比如微服务、消息队列等。
  • Q2:使用观察者模式的缺点有哪些,怎么解决?
    • 使用观察者模式时可以给观察者设置优先级,按照优先级排序通知,解决通知无序问题。
    • 对于内存泄漏问题,可以使用弱引用存储观察者,还可以在观察者生命周期结束时主动调用detach方法移除观察者引用。
    • 在update方法中增加状态变更标记,避免重复触发通知逻辑,可以解决循环通知问题。
    • 当观察者模式使用同步通知遇到性能瓶颈时,可以使用线程池异步通知,主题状态变化时异步调用观察者的update方法。
  • Q3:观察者模式中有哪些设计原则?
    • 依赖倒置原则:观察者模式的主题和观察者都依赖抽象而不依赖具体实现;
    • 开放闭合原则:新增观察者时无需修改主题代码,对扩展开放、对修改闭合;
    • 单一职责原则:观察者模式的主题只负责维护状态和通知,观察者只负责处理通知。
Last Updated: 3/10/2026, 6:08:48 PM

← 装饰器模式

评论

验证登录状态...

侧边栏
夜间
卡码简历
代码随想录
卡码投递表🔥
2026群
添加客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记
鼓励/支持/赞赏Carl
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪