zoukankan      html  css  js  c++  java
  • 观察者模式(Observer)

    一、观察者模式介绍

    观察者模式(发布-订阅模式):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象。使它们能够自动更新自己。

    例如:

    1、游戏中的例子:游戏中,当进入新人的时候,大喇叭会通知所有的玩家。所有的游戏玩家是订阅者,而大喇叭就是一个发布者

    2、QQ群聊天中:在群里聊天的时候,你发一句话,大家都可以看到。那么,这个群里的人就是订阅者,而通过群发消息的服务器则作为了一个发布者

    观察者模式UML图

    Subject(抽象主题):抽象主题中把所有观察者对象的引用都保存在一个集合里,每个主题都可以有任何数量的观察者,这个主题提供一个接口。

    来增加或者删除具体的观察者对象。

    ConcreteSubject(具体主题):具体主题中就操作了具体的观察者对象,将有关状态存入具体的观察者对象中,假如具体主题内部状态发生了改变。

    则给所有注册过的观察者发出更新的通知。然后具体的观察者则更新自己的状态。

    Observer(抽象观察者):为所有观察者定义一个接口,在得到主题的通知时则更新自己。

    ConcreteObserver(具体观察者):实现抽象观察者定义的接口,自身的状态随着主题的更新而更新。

    二、观察者模式代码实现

    定义一个抽象观察者接口:相当于玩家角色,也可以使用抽象类。抽象观察者中只提供一个更新状态的方法,所有实现该抽象观察者的具体观察者,需要随着更新

    1
    2
    3
    4
    5
    //抽象观察者:为所有的观察者定义一个接口
    public interface Observer {
        //传入主题对象,得到主题对象的通知时更新自己
        void upadte(Subject subject);
    }

    具体的观察者:相当于具体的游戏玩家

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //具体的观察者
    public class ConcreteObserver implements Observer{
        private int state;//state对象需要和subject中的state保持一致
        @Override
        public void upadte(Subject subject) {
            //当目标对象(主题对象)状态发生改变时,观察者也发生改变。
            this.state = ((ConcreteSubject)subject).getState();    
        }
        public int getState() {
            return state;
        }
        public void setState(int state) {
            this.state = state;
        }
    }

    定义主题对象,主题对象中存储了所有该主题下的所有观察者,并提供增加和删除观察者的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //抽象主题对象:把所有观察者对象都保存到一个集合里,每个主题都可以任何数量的观察者
    //抽象主题对象提供了增加和删除观察者对象的方法
    public class Subject {
        //保存该主题下所有的观察者
        protected List<Observer> list = new ArrayList<Observer>();
        //添加观察者
        public void registerObserver(Observer observer){
            list.add(observer);
        }
        //删除观察者
        public void removerObserver(Observer observer){
            list.remove(observer);
        }
        //通知所有观察者
        public void notifyAllObserver(){
            for (Observer obs : list) {
                obs.upadte(this);//更新当前主题(subject)对象的信息到所有观察者中
            }
        }
    }

    开始定义具体的主题对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //具体的主题对象
    public class ConcreteSubject extends Subject {
        private int state;//默认状态为0
        public int getState() {
            return state;
        }
        public void setState(int state) {
            this.state = state;
            //当修改了主题对象状态时,通知所有观察者
            this.notifyAllObserver();//通知所有观察者
        }
    }

    客户端测试代码:

    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
    public static void main(String[] args) {
        //构建目标对象(主题对象)
        ConcreteSubject subject = new ConcreteSubject();
        //具体的观察者
        ConcreteObserver c1 = new ConcreteObserver();
        ConcreteObserver c2 = new ConcreteObserver();
        ConcreteObserver c3 = new ConcreteObserver();
        c1.setState(10);//设置观察者1的状态为10
        c2.setState(20);//设置观察者2的状态为20
        c3.setState(30);//设置观察者3的状态为30
        //将三个观察者假如到观察者队列中
        subject.registerObserver(c1);
        subject.registerObserver(c2);
        subject.registerObserver(c3);
        //查看未修改时的状态
        System.out.println(c1.getState());
        System.out.println(c2.getState());
        System.out.println(c3.getState());
         
        System.out.println("-------------修改后的状态-------------");
        //改变目标对象的状态
        subject.setState(1000);
        //查看观察者对象的状态
        System.out.println(c1.getState());
        System.out.println(c2.getState());
        System.out.println(c3.getState());
    }

    测试结果:

                    10

                    20

                    30

                    -------------修改后的状态-------------

                    1000

                    1000

                    1000

    这里我们是自己定义的Observer和Subject。其实java中已经帮我们写好了这两个目标对象接口和观察者接口

    java.util.Observable 这个类就相当于我们自己定义的subject类
    java.util.Observer   接口

    下面,使用jdk提供的两个接口来实现一个观察者模式demo

    定义一个具体的目标对象(原代码中的ConcreteSubject):继承java.util.Observable类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.util.Observable;
     
    //具体的目标对象
    public class ConcreteSubject extends Observable{
        private int state;
        public void updateState(int s){
            state = s;//目标对象发生了改变
            setChanged();//Observable类中的setChanged()方法,表示目标对象发生了更改
            notifyObservers(state);//通知所有观察者对象
        }
        public int getState() {
            return state;
        }
        public void setState(int state) {
            this.state = state;
        }
    }

    定义具体的观察者:实现java.util.Observer接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.util.Observable;
    import java.util.Observer;
     
    //具体的观察者
    public class ConcreteObserver implements Observer{
        private int state;//具体观察者中的状态和目标对象的状态保持一致
        @Override
        public void update(Observable o, Object arg) {
            state = ((ConcreteSubject)o).getState();
        }
        public int getState() {
            return state;
        }
        public void setState(int state) {
            this.state = state;
        }
    }

    客户端测试代码:使用addObserver来添加观察者到集合中

    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
    public static void main(String[] args) {
        //构建目标对象(主题对象)
        ConcreteSubject subject = new ConcreteSubject();
        //具体的观察者
        ConcreteObserver c1 = new ConcreteObserver();
        ConcreteObserver c2 = new ConcreteObserver();
        ConcreteObserver c3 = new ConcreteObserver();
        c1.setState(10);//设置观察者1的状态为10
        c2.setState(20);//设置观察者2的状态为20
        c3.setState(30);//设置观察者3的状态为30
        //将三个观察者假如到观察者队列中
    //      subject.registerObserver(c1);
    //      subject.registerObserver(c2);
    //      subject.registerObserver(c3);
        subject.addObserver(c1);//Observer接口中使用addObserver来添加观察者对象到集合中
        subject.addObserver(c2);
        subject.addObserver(c3);
        //查看未修改时的状态
        System.out.println(c1.getState());
        System.out.println(c2.getState());
        System.out.println(c3.getState());
         
        System.out.println("-------------修改后的状态-------------");
        //改变目标对象的状态
    //      subject.setState(1000);
        subject.updateState(1000);//使用刚才定义的updateState方法来更新状态
        //查看观察者对象的状态
        System.out.println(c1.getState());
        System.out.println(c2.getState());
        System.out.println(c3.getState());
    }

    结果:

            10

            20

            30

            -------------修改后的状态-------------

            1000

            1000

            1000

    三、总结

    ​开发中常见场景:

    1.聊天室程序,服务器转发信息给所有客户端

    2.网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发

    3.邮件订阅

    4.Servlet中,监听器的实现

    5.Android的广播机制

    6.JDK的AWT中事件处理模型



    Java23种设计模式学习笔记【目录总贴】

    参考资料:

      大话设计模式(带目录完整版).pdf

      HEAD_FIRST设计模式(中文版).pdf

      尚学堂_高淇_java300集最全视频教程_【GOF23设计模式】

  • 相关阅读:
    (转)JVM参数的说明、简单记法和GC调优策略
    深度学习论文翻译解析(十二):Fast R-CNN
    深度学习论文翻译解析(十一):OverFeat: Integrated Recognition, Localization and Detection using Convolutional Networks
    vue中组建的创建和使用
    CountDownLatch的理解和使用
    java多线程并发编程中对一些概念和关键字的理解
    spring中访问变量的用法
    mysql中group by优化
    vue中实现标题的国际化
    mysql中的覆盖索引,二级索引和索引排序
  • 原文地址:https://www.cnblogs.com/cxxjohnson/p/6403944.html
Copyright © 2011-2022 走看看