观察者模式

一、什么是观察者模式

​ 观察者模式 (Observer Pattern) 又叫发布-订阅模式 (Publish/Subscribe),定义一种一对多的依赖关系,一个主题对象 (被观察对象) 可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被更新,属于行为型模式。

​ 观察者模式的核心是将观察者与主题对象解耦,以类似于消息、广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。

二、观察者模式包含的角色

观察者模式的通用类图:
在这里插入图片描述
抽象主题 (Subject):指被观察的对象 (Observable)。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法
具体主题 (Concrete Subject):具体被观察者,当其内部状态变化时,会通知已注册的观察者
抽象观察者 (Observer):定义了响应通知的更新方法
具体观察者 (Concrete Observer):在得到状态更新时,会自动做出响应

三、观察者模式通用写法

抽象主题(Subject)的内部变化会通知到感兴趣的观察者,观察者会根据通知做出相应处理

  1. 创建抽象主题角色 ISubject
       /**
        * @ClassName: ISubject
        * @description: 抽象主题角色,被观察者对象
        * @author: zdp
        * @create: 2022-09-10 15:20
        **/
       public interface ISubject<T> {
       
            /**
             * 注册观察者
             *
             * @Author  zdp
             * @Date 2022/9/10 15:35
             * @param observer	observer
             * @return 新增结果
             */
           boolean addObserver(IObserver<T> observer);
       
            /**
             * 移除观察者
             *
             * @Author  zdp
             * @Date 2022/9/10 15:35
             * @param observer	observer
             * @return 移除结果
             */
           boolean remove(IObserver<T> observer);
       
            /**
             * 被观察者内部状态发生变动通知观察者的方法
             * @Author  zdp
             * @Date 2022/9/10 15:35
             */
           void notify(T event);
       
       }
    
  2. 创建具体主题角色 ConcreteSubject
     /**
        * @ClassName: ConcreteSubject
        * @description: 具体主题角色,具体被观察对象
        * @author: zdp
        * @create: 2022-09-10 15:21
        **/
       public class ConcreteSubject<T> implements ISubject<T> {
       
           /**
            * 观察者注册容器
            */
           List<IObserver<T>> observers = new LinkedList<>();
       
           @Override
           public boolean addObserver(IObserver<T> observer) {
               if (observers.contains(observer)) {
                   return true;
               }
               return observers.add(observer);
           }
       
           @Override
           public boolean remove(IObserver<T> observer) {
               if (observers.contains(observer)) {
                   return observers.remove(observer);
               }
               return true;
           }
       
           @Override
           public void notify(T event) {
               if (!observers.isEmpty()) {
                   for (IObserver<T> observer : observers) {
                       observer.update(event);
                   }
               } else {
                   System.err.println("当前没有观察者...");
               }
           }
       }
    
  3. 创建抽象观察者 Observer
     /**
        * @ClassName: IObserver
        * @description: 抽象观察者
        * @author: zdp
        * @create: 2022-09-10 15:21
        **/
       public interface IObserver<T> {
       
            /**
             * 观察者接收通知的业务处理方法
             *
             * @Author  zdp
             * @Date 2022/9/10 15:44
             * @param event 被观察者状态变动发送的通知参数
             */
            void update(T event);
       }
    
  4. 创建具体观察者 ConcreteObserver
     /**
        * @ClassName: ConcreteObserver
        * @description: 具体观察者角色
        * @author: zdp
        * @create: 2022-09-10 15:22
        **/
       public class ConcreteObserver<T> implements IObserver<T> {
       
           @Override
           public void update(T event) {
               System.out.println("具体观察者 ConcreteObserver 接收到消息: " + event.toString());
           }
       
       }
    
  5. 通用写法验证
    /**
        * @ClassName: Test
        * @description: 观察者模式通用写法测试
        * @author: zdp
        * @create: 2022-09-10 15:24
        **/
       public class Test {
       
           public static void main(String[] args) throws InterruptedException {
               //主题对象(被观察者对象)
               ISubject<String> subject = new ConcreteSubject<>();
               //抽象观察者
               IObserver<String> observer = new ConcreteObserver<>();
               //注册观察者
               subject.addObserver(observer);
               //主题对象变动向观察者推送消息
               subject.notify("消息过来了....");
       
               //移除观察者
               subject.remove(observer);
               //再次推送
               subject.notify("消息过来了....");
           }
       }
    

四、观察者模式在业务中的使用举例

在问答系统中学生可以向老师提问,当设置了指定老师回答,对应老师就能够收到相应的通知,在现有的api中,jdk和guava将观察者模式分别为我们封装了现成的api,我们直接使用即可

  1. 使用Jdk为我们封装的方法
    1. 抽象主题对象是Jdk提供的Observable

    2. 创建主题对象(被观察者对象)Student

       /**
             * @ClassName: Student
             * @description: 具体主题角色,学生对象
             * @author: zdp
             * @create: 2022-09-10 16:35
             **/
            @Data
            @NoArgsConstructor
            @AllArgsConstructor
            public class Student extends Observable {
            
                private String studentName;
            
                public void pushQuestion(Question question){
                    System.out.println("学生【" + this.studentName + "】在系统中推送了一个问题,请您及时查看~");
                    //设置主题对象内部状态发生变动
                    setChanged();
                    //通知观察者 (老师)
                    notifyObservers(question);
                }
            }
            /**
             * @ClassName: Question
             * @description: 通知传递参数
             * @author: zdp
             * @create: 2022-09-10 16:42
             **/
            @Data
            @NoArgsConstructor
            @AllArgsConstructor
            class Question {
                private String content;
                private String studentName;
            }
      
    3. 抽象观察者是Jdk提供的Observer

    4. 创建具体观察者对象 Teacher

            /**
             * @ClassName: Teacher
             * @description: 观察者对象
             * @author: zdp
             * @create: 2022-09-10 16:43
             **/
            @Data
            @AllArgsConstructor
            public class Teacher implements Observer {
            
                private String teacherName;
            
                /**
                 * @param o 具体的被观察对象
                 * @param arg 通知参数
                 */
                @Override
                public void update(Observable o, Object arg) {
                    Student student = (Student) o;
                    Question question = (Question) arg;
                    System.out.println(this.teacherName + " 您好~ 您收到来自学生【" + student.getStudentName()+ "】的提问! 问题是:" + question.getContent() + " 请您及时解答~");
                }
            }
            ```
      
      
    5. 测试

      public class Test {
      
          public static void main(String[] args) {
              //创建具体的被观察者对象
              Student student = new Student("zdp");
              //注册观察者
              Teacher one = new Teacher("Teacher One..");
              Teacher two = new Teacher("Teacher Two..");
              student.addObserver(one);
              student.addObserver(two);
              //推送通知
              Question question = new Question("什么是观察者模式? ", student.getStudentName());
              student.pushQuestion(question);
          }
      }
      

在这里插入图片描述
2. 使用Guava为我们封装的api

  1. 创建观察者Teacher
    /**
     * @ClassName: Teacher
     * @description: 观察者对象
     * @author: zdp
     * @create: 2022-09-10 16:43
     **/
    @Data
    @AllArgsConstructor
    public class Teacher {
    
        private String teacherName;
    
        @Subscribe
        public void update(Student stu) {
            System.out.println(this.teacherName + " 您好~ 您收到来自学生【" +
                    stu.getStudentName()+ "】的提问! 问题是:" + stu.getQuestion().getContent() + " 请您及时解答~");
        }
    }
    
  2. 创建通知参数
    /**
     * @ClassName: Student
     * @description: 具体主题角色,学生对象
     * @author: zdp
     * @create: 2022-09-10 16:35
     **/
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
    
        private String studentName;
    
        private Question question;
    
        /**
         * @ClassName: Question
         * @description: 通知参数
         * @author: zdp
         * @create: 2022-09-10 16:42
         **/
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        static class Question {
            private String content;
        }
    
    }
    
  3. 测试类
    /**
     * @ClassName: Test
     * @description: guava
     * @author: zdp
     * @create: 2022-09-10 17:08
     **/
    public class Test {
    
        public static void main(String[] args) {
    
            EventBus eventBus = new EventBus();
    
            Teacher one = new Teacher("Teacher One....");
            Teacher two = new Teacher("Teacher Two....");
            //注册观察者
            eventBus.register(one);
            eventBus.register(two);
    
            Student student = new Student("zdp", new Student.Question("什么是观察者模式? "));
    
            //推送消息到感兴趣的观察者
            eventBus.post(student);
        }
    }
    

在这里插入图片描述

五、观察者模式的优缺点

  • 优点
    • 观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则
    • 实现了一对多的通讯机制,支持事件注册机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知
  • 缺点
    • 如果观察者数量过多,则事件通知会耗时较长
    • 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件
    • 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐