• 03-JAVA设计模式-观察者模式


    观察者模式

    什么是观察者模式

    Java中的观察者模式是一种常见的设计模式,它允许对象(观察者)订阅另一个对象(被观察者)的状态变化,并在状态变化时自动得到通知。

    核心:
    观察者模式主要用于1:N的通知。当一个对象(目标对象)的状态发生变化时,它需要及时告知一系列对象(观察者对象)令他们做出响应。

    通知观察者的方式:

    • 推:
      • 每次都会把通知以广播的方式发送给所有观察者,所有观察者只能被动接收。
    • 拉:
      • 观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容都可以自主决定。

    优点:

    • 解耦:观察者模式降低了对象之间的耦合度,使得它们可以独立地改变和复用。
    • 扩展性:新的观察者可以很容易地加入到现有系统中,而无需修改其他部分的代码。
    • 灵活性:观察者模式允许在运行时动态地改变观察者和被观察者的关系。

    缺点:

    • 开发和调试复杂:由于系统中存在多个观察者和被观察者,开发和调试可能会变得相对复杂。
    • 性能问题:如果观察者数量庞大,或者被观察者状态改变频繁,可能会导致性能下降。
    • 消息通知顺序:在Java中,消息的通知一般是顺序执行的,一个观察者卡顿可能会影响整体的执行效率。

    常见场景:

    • 图形用户界面(GUI)开发:在GUI应用程序中,当用户与界面进行交互(例如点击按钮或拖动滑块)时,界面的某些部分可能需要更新以反映这些变化。观察者模式允许这些部分(观察者)自动响应这些事件,而无需手动调用更新方法。
    • 游戏开发:在游戏中,玩家和NPC的行为,如移动、攻击、受伤等,都可能触发一系列事件。观察者模式可以用来注册和通知这些事件的监听器,以便游戏逻辑能够实时响应并更新游戏状态。
    • 消息传递系统:在分布式系统或微服务架构中,组件之间需要传递消息以实现通信和协作。观察者模式允许一个组件(被观察者)在产生消息时通知所有订阅了该消息的组件(观察者),从而实现高效的消息传递。
    • 实时数据监控:在需要实时跟踪和响应数据变化的场景中,如股票价格监控、传感器数据读取等,观察者模式可以帮助实现当数据变化时自动触发相应的处理逻辑。
    • 状态管理:当应用程序中某个对象的状态发生变化,并且需要通知其他对象进行相应的处理时,可以使用观察者模式。例如,一个订单状态的改变可能需要通知库存系统、支付系统等多个系统进行相应的操作。

    案例1

    推方式实现,以广播方式发送消息所有观察者

    UML

    在这里插入图片描述

    实现步骤:

    • 创建观察者接口,定义通过被观察者更新状态接口
    • 创建被观察者,被观察者持有所有观察者的引用,提供注册、移除、通知所有观察者的方法,定义状态属性,提供get/set方法set方法中默认通知所有观察者

    实现代码

    Observer.java

    // 观察者接口
    public interface Observer {
        void update(Subject subject);
    }
    
    • 1
    • 2
    • 3
    • 4

    Subject.java

    import java.util.ArrayList;
    import java.util.List;
    
    // 被观察者
    public class Subject {
        // 定义存储观察者的集合
        private List<Observer> observerList = new ArrayList<Observer>();
        // 定义状态属性
        private String state;
    
        // 注册观察者
        public void registerObserver(Observer observer){
            observerList.add(observer);
        }
    
        // 移除观察者
        public void removeObserver(Observer observer){
            observerList.remove(observer);
        }
    
        // 通知所有观察者
        public void notifyObservers(){
            for(Observer observer : observerList){
                observer.update(this);
            }
        }
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
            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

    ObserverA.java

    // 观察者A
    public class ObserverA implements Observer{
        @Override
        public void update(Subject subject) {
            System.out.printf("观察者A接收到消息-state:%s%n",subject.getState());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ObserverB.java

    // 观察者B
    public class ObserverB implements Observer{
        @Override
        public void update(Subject subject) {
            System.out.printf("观察者B接收到消息-state:%s%n",subject.getState());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    TestClient.java

    public class TestClient {
        public static void main(String[] args) {
            Subject subject = new Subject();
            subject.registerObserver(new ObserverA());
            subject.registerObserver(new ObserverB());
            subject.setState("START");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行结果:

    在这里插入图片描述

    案例2

    使用java.util提供的Observable,Observer类实现案例1。

    该方式已在1.9版本中移除,不再推荐使用

    实现代码

    ConcreteSubject.java

    import java.util.Observable;
    
    // 目标对象
    public class ConcreteSubject extends Observable {
    
        private String state;
    
        public void setState(String state) {
            this.state = state;
            // 表示目标对象已被改变
            setChanged();
            // 通知所有观察者
            notifyObservers();
        }
    
        public String getState() {
            return state;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ObserverA.java

    import java.util.Observable;
    import java.util.Observer;
    
    /**
     * 观察者A
     *
     * @author Anna.
     * @date 2024/4/25 16:15
     */
    public class ObserverA implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            System.out.printf("观察者A接收到消息-state:%s%n", ((ConcreteSubject) o).getState());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ObserverB.java

    import java.util.Observable;
    import java.util.Observer;
    
    // 观察者B
    public class ObserverB implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            System.out.printf("观察者B接收到消息-state:%s%n", ((ConcreteSubject) o).getState());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    TestClient.java

    public class TestClient {
        public static void main(String[] args) {
            ConcreteSubject subject = new ConcreteSubject();
            subject.addObserver(new ObserverA());
            subject.addObserver(new ObserverB());
    
            subject.setState("STOP");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行结果:
    在这里插入图片描述

    案例3

    通过拉方式,实现观察者主动拉取被观察者当前状态

    UML

    在这里插入图片描述

    实现步骤:

    • 创建被观察者Subject,定义状态属性并提供get/set方法
    • 定义观察者接口类,提供拉取状态接口
    • 创建具体的观察者,实现观察者接口,通过持有被观察者的引用,主动拉取被观察者当前状态

    实现代码

    Subject.java

    // 被观察者
    public class Subject {
        // 定义当前状态
        private String state;
        public String getState() {
            return state;
        }
        public void setState(String state) {
            this.state = state;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Observer.java

    // 观察者接口
    public interface Observer {
        // 定义拉取信息的接口
        void update();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ConcreteObserver.java

    // 观察者具体实现
    public class ConcreteObserver implements Observer {
        // 定义被观察者,持有被观察者的引用
        private Subject subject;
        public ConcreteObserver(Subject subject) {
            this.subject = subject;
        }
        @Override
        public void update() {
            System.out.printf("观察者主动拉取状态-state:%s%n ", subject.getState());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    TestClient.java

    public class TestClient {
        public static void main(String[] args) {
            // 创建被观察者
            Subject subject = new Subject();
            // 创建观察者
            ConcreteObserver observer = new ConcreteObserver(subject);
            // 修改状态
            subject.setState("RUN");
            // 观察者主动拉取
            observer.update();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行结果:

    在这里插入图片描述

    gitee源码

    git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

  • 相关阅读:
    【直播笔记0628】 高频面试并发的本质:JAVA程序员应该掌握的并发知识
    401、基于51单片机的电压源(2挡,LCD1602,36V)(程序+Proteus仿真+原理图+流程图+元器件清单+硬件系统框图+配套资料等)
    MATLAB算法实战应用案例精讲-【智能优化算法】黑寡妇算法-BWO(附matlab代码)
    如何正确开启ruoyi-vue-pro的支付模块,
    网页元素定位秘籍:从HTML探秘到Python自动化实战20240626
    【clickhouse】一个性能问题,把一个中间件从头到位翻了个遍
    js滚动条触底加载更多#js懒加载数据#步骤条布局懒加载
    从JSON数据到Pandas DataFrame:如何解析出所需字段
    使用Spring-data-jpa
    河南直销系统开发对直销行业有何帮助?
  • 原文地址:https://blog.csdn.net/u014331138/article/details/138193660