• 设计模式详解(十六)——观察者模式


    观察者模式简介

    观察者模式定义
    观察者模式(Observer Pattern)是一种对象行为模式,用于建立对象之间的一对多依赖关系。观察者模式,又叫发布-订阅模式。在这种模式中,有一个称为主题(Subject)的对象,它维护了一组依赖于它的观察者(Observers)。当主题的状态发生变化时,它会自动通知所有的观察者,使它们能够自动更新自己,以保持与主题状态的一致性。在观察者模式中,目标对象(主题)负责维护其依赖的观察者列表,并在其状态发生改变时主动通知这些观察者。在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式的核心思想是松耦合,主题和观察者之间相互独立,彼此不直接依赖,从而使系统更加灵活和易于扩展。主题和观察者之间的关系是动态的,观察者可以随时加入或退出,而不会影响其他对象之间的通信。

    观察者模式包含以下角色:

    1. 主题(Subject):也称为被观察者,它维护一组观察者对象,并提供添加、删除和通知观察者的方法。主题的状态发生变化时,会通知所有注册的观察者。它定义了通知方法,当需要通知观察者时,会遍历观察者列表并调用每一个观察者的更新方法。
    2. 观察者(Observer):观察主题的对象,当主题的状态发生变化时,观察者会接收到通知并进行相应的更新操作。观察者会将自己注册到被观察者的列表中,以便在被观察者状态发生改变时能够收到通知观察者是一个接口或抽象类,它包含了一个更新自己的抽象方法,当接到被观察者的变化通知时,这个方法将被调用。
    3. 具体主题(ConcreteSubject):主题的具体实现类,它实现Subject接口,具体实现注册、移除、通知观察者等方法,负责维护具体的观察者对象列表,并在状态发生变化时通知观察者。
    4. 具体观察者(ConcreteObserver):观察者的具体实现类,实现Observer接口,具体实现更新自己的方法,以便在接收到主题通知时如何更新自身状态的具体逻辑。

    观察者模式优缺点:
    优点:

    1. 解耦性:观察者模式可以将主题和观察者之间的关系解耦,因为目标对象(主题)不需要知道具体有哪些观察者,只需要维护一个观察者列表,并在状态发生改变时通知它们。这使得两者可以独立地改变和复用,从而更容易维护和扩展。
    2. 可扩展性:由于主题和观察者之间的关系是松耦合的,因此可以随时增加新的观察者或删除现有的观察者,而不需要修改主题的代码。
    3. 易于维护:观察者模式使系统中的对象更加模块化,使代码更易于理解和维护,同时也降低了代码的复杂度。
    4. 实现广播通信:观察者模式也可以看作是一种发布-订阅模式,该模式支持一对多的依赖关系,当被观察者状态改变时,可以通知多个观察者,实现广播通信的效果。使得主题和观察者之间的通信更加灵活和可靠。
    5. 符合开闭原则:对于扩展是开放的,对于修改是封闭的。新的观察者可以很容易地添加到系统中,而无需修改被观察者的代码。

    缺点:

    1. 导致内存泄漏:如果观察者没有正确移除,或者主题持有对观察者的强引用,可能导致观察者无法被正确回收,从而造成内存泄漏。
    2. 广播开销:当主题状态发生变化时,需要通知所有的观察者,所以当主题的状态改变非常频繁,且如果观察者数量很大或者更新操作很耗时,可能会造成性能上的开销和导致系统性能下降。因为每次状态改变都需要遍历观察者列表并调用它们的更新方法。
    3. 循环依赖:如果观察者之间存在循环依赖,可能导致系统出现逻辑上的混乱,需要谨慎设计观察者之间的关系。
    4. 观察者之间缺乏通信:观察者模式中的观察者之间是独立的,它们之间不能直接通信。如果需要观察者之间进行交互,可能需要额外的机制来实现。

    使用场景

    1. GUI应用程序:在GUI应用程序中,经常需要实现界面元素和数据模型之间的同步更新,观察者模式可以很好地解决这种场景。
    2. 事件处理系统:观察者模式常用于事件处理系统中,当事件发生时,通知所有注册的观察者进行相应的处理。
    3. 消息通知系统:在消息通知系统中,观察者模式被广泛采用。当发布者发布新消息时,所有订阅该消息的观察者都会收到通知并执行相应的操作。这种模式常见于聊天应用、社交媒体平台等需要实时消息传递的场景。
    4. 股票市场:股票市场中的股票价格变动可以看作是主题的状态变化,而投资者可以看作是观察者,他们需要及时获取股票价格的变动情况。
    5. 实时日志记录:在实时日志记录系统中,日志记录器作为被观察者,而观察者可以是日志分析器、报警系统等。当日志发生变化时,观察者会收到通知并执行相应的操作,如生成报告、发送警报等。

    以下举一个观察者模式的例子:
    下面通过一个简单的例子来演示。
    比如生活中,微信公众号是大家都普遍使用的东西,当用户关注了对应的公众号后,该公众号每次发新的东西出来时,关注了该公众号的用户就可以接收到该公众号发出的通知,这就是用到了观察者模式,用户作为观察者,公众号作为主题。

    创建主题(Subject)

    /**
     * 主题(观察对象)
     */
    public interface Subject {
    
        //添加观察者
        void add(Observer observer);
    
        //删除观察者
        void delete(Observer observer);
    
        //通知观察者更新消息
        void notify(String message);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建具体主题(ConcreteSubject)

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 具体主题(具体观察对象)
     */
    public class ConcreteSubject implements Subject{
    
        //定义一个集合,用来存储观察者对象
        private List<Observer> userList = new ArrayList<>();
    
        @Override
        public void add(Observer observer) {
            userList.add(observer);
        }
    
        @Override
        public void delete(Observer observer) {
            userList.remove(observer);
        }
    
        @Override
        public void notify(String message) {
            //遍历集合
            for (Observer observer : userList) {
                observer.update(message);
            }
        }
    }
    
    • 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

    创建观察者(Observer)

    /**
     * 观察者
     */
    public interface Observer {
        void update(String message);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    创建具体观察者(ConcreteObserver)

    /**
     * 具体观察者
     */
    public 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建客户端(Client)

    /**
     * 客户端
     */
    public class Client {
        public static void main(String[] args) {
            // 创建公众号(具体主题)
            ConcreteSubject concreteSubject = new ConcreteSubject();
    
            // 用户订阅公众号,及将观察者加入到该公众号的用户列表中
            concreteSubject.add(new ConcreteObserver("小明"));
            concreteSubject.add(new ConcreteObserver("小红"));
            concreteSubject.add(new ConcreteObserver("小蓝"));
    
            // 公众号发新的文章了,需要通知关注该公众号的所有用户
            concreteSubject.notify("公众号发布了新的文章《如何学好java》,快去看看吧");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出结果如下所示:

    通知该公众号关注者小明,公众号发布了新的文章《如何学好java》,快去看看吧
    通知该公众号关注者小红,公众号发布了新的文章《如何学好java》,快去看看吧
    通知该公众号关注者小蓝,公众号发布了新的文章《如何学好java》,快去看看吧
    
    • 1
    • 2
    • 3

    在上述例子中,我们先定义了主题对象接口 Subject 和观察者对象接口 Observer。然后我们实现了具体的主题对象类 ConcreteSubject 和具体的观察者对象类 ConcreteObserver,在具体主题对象类中添加、删除和通知观察者对象,更改状态值并且通知所有的观察者对象。在具体观察者对象类 ConcreteObserver 中,实现更新自身状态的逻辑和输出通知语句。客户端Client中我们创建主题对象,并创建三个观察者对象并将它们添加到主题对象中,再调用主题对象的notify方法,触发对所有观察者对象发送通知。因此当后面更多的用户关注该公众号,只要将新的观察者加入到该公众号的用户列表中,当公众号发布新的文章时,就会统一对这些关注该公众号的用户发送通知了,实现广播通信的效果。

    总而言之:
    观察者模式是一种实现对象间一对多依赖关系的设计模式。在这种模式中,有一个称为主题的对象,它维护了一组依赖于它的观察者。当主题的状态发生变化时,它会自动通知所有的观察者,使它们能够自动更新自己,以保持与主题状态的一致性。它允许被观察者对象在状态改变时,自动通知其注册的观察者对象,实现消息的广播与通信。观察者模式的核心思想是松耦合,主题和观察者之间相互独立,彼此不直接依赖,从而使系统更加灵活和易于扩展。观察者模式包含四个主要角色:主题、观察者、具体主题和具体观察者。其优点包括解耦性、可扩展性和易于维护,缺点则可能导致内存泄漏和广播开销。
    总之,观察者模式是一种强大的设计模式,适用于事件处理、消息通知、股票市场分析、实时日志记录、游戏开发以及团队协作等多种场景。通过合理使用观察者模式,我们可以构建出更加灵活、可扩展和高效的软件系统。


    以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git

  • 相关阅读:
    SpringBoot文件上传
    VLQ & Base64 VLQ 编码方式的原理及代码实现
    某职业混子的学习路线
    SpringSecurity用户认证设置用户名和密码的三种方式
    一文带你了解MySQL之redo日志
    渗透测试-基于浏览器的口令暴破与图形验证码识别
    Go与C/C++中的堆和栈比较
    利用根升余弦滤波器和整数倍内插的多相结构生成含采样频偏的过采样信号
    【微服务】Alibaba Cloud Linux环境下Docker以及MySQL安装
    微服务调用没有返回值,无法组成对象,但是会有feign的信息
  • 原文地址:https://blog.csdn.net/qq_44307209/article/details/138080849