美文网首页IT@程序员猿媛
【重温设计模式】JAVA中对观察者模式的支持

【重温设计模式】JAVA中对观察者模式的支持

作者: 叫我不矜持 | 来源:发表于2019-05-03 21:54 被阅读0次

    前言

    本文将分为以下三个部分介绍观察者模式

    • 1.观察者模式介绍
    • 2.观察者模式示例
    • 3.Java中的观察者模式

    一.观察者模式介绍

    当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    image

    观察者模式的三要素:观察者,被观察者,事件

    优点:
    1、解耦,被观察者只知道观察者列表「抽象接口」,被观察者不知道具体的观察者
    2、被观察者发送通知,所有注册的观察者都会收到信息「可以实现广播机制」

    缺点:
    1、如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
    2、观察者知道被观察者发送通知了,但是观察者不知道所观察的对象具体是如何发生变化的
    3、如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃

    二.观察者模式示例

    1.警察抓小偷

    • 1)首先是被观察者(罪犯)接口的定义
    //罪犯
    public interface Criminal {
        //罪犯被顶上了
        void spotted(Police police);
        //罪犯的犯罪行为
        void crime(String illegalAction);
        String getName();
        String getIllegalAction();
    }
    
    

    2)接下来是观察者(警察)接口的定义

    //警察
    public interface Police {
        //警察的行为
        void action(Criminal criminal);
    }
    
    
    • 3)然后是被观察者(小偷)的实现类
    //小偷
    public class Thief implements Criminal {
        //盯上罪犯的警察集合
        private LinkedList<Police> polices = new LinkedList<>();
        //罪犯姓名
        private String name;
        //犯罪行为
        private String illegalAction;
    
        //单例模式
        public static Thief getInstance(){
            return InnerClass.THIEF;
        }
        //静态内部类单例模式
        private static class InnerClass{
            private static final Thief THIEF = new Thief("小偷");
        }
    
        private Thief(String name) {
            this.name = name;
        }
    
        @Override
        //被盯上了
        public void spotted(Police police) {
            if (!polices.contains(police)) {
                polices.add(police);
            }
        }
    
        @Override
        //犯罪
        public void crime(String illegalAction) {
            this.illegalAction = illegalAction;
            for (Police police : polices) {
                police.action(this);
            }
        }
    
        @Override
        //罪犯姓名
        public String getName() {
            return name;
        }
    
        @Override
        //犯罪行为
        public String getIllegalAction() {
            return illegalAction;
        }
    }
    
    
    • 4)最后是观察者(警察)的实现类

    武警

    //武警
    public class ArmedPolice implements Police {
    
        public ArmedPolice(Criminal criminal) {
            System.out.println(criminal.getName()+"被人民武警盯上了");
            //这里调用罪犯的spotted方法,说明罪犯被顶上了
            criminal.spotted(this);
        }
    
        @Override
        public void action(Criminal criminal) {
            System.out.println("武警发现了"+criminal.getName()+criminal.getIllegalAction());
        }
    }
    

    朝阳群众

    //朝阳群众
    public class MassesPolice implements Police {
        public MassesPolice(Criminal criminal) {
            System.out.println(criminal.getName()+"被朝阳大妈盯上了");
            //这里调用罪犯的spotted方法,说明罪犯被顶上了
            criminal.spotted(this);
        }
    
        @Override
        public void action(Criminal criminal) {
            System.out.println("朝阳大妈发现了"+criminal.getName()+criminal.getIllegalAction());
        }
    }
    

    公安

    //公安
    public class SecurityPolice implements Police {
    
        public SecurityPolice(Criminal criminal) {
            System.out.println(criminal.getName()+"被公安盯上了");
            //这里调用罪犯的spotted方法,说明罪犯被顶上了
            criminal.spotted(this);
        }
    
        @Override
        public void action(Criminal criminal) {
            System.out.println("公安发现了"+criminal.getName()+criminal.getIllegalAction());
        }
    }
    
    • 5)测试
    public class TestObserver {
        public static void main(String[] args) {
            Thief thief = Thief.getInstance();
            ArmedPolice armedPolice = new ArmedPolice(thief);
            SecurityPolice securityPolice = new SecurityPolice(thief);
            MassesPolice massesPolice = new MassesPolice(thief);
    
            System.out.println("——————————————————————————");
            thief.crime("盗窃");
    
        }
    }
    
    运行效果截图.png

    2.小明和妈妈

    生活中的一个例子,小明妈妈做好了饭,让小明去地里叫他爸回来吃饭,小明说好的我马上去,过了半个小时小明和他爸一起来了,小明给妈妈的说:“妈,爸回来了”,妈妈说:“好的我知道了,让你爸洗洗手吃饭吧”。
    在这一过程中,小明是被观察者,小明他妈是观察者。
    小明观察到他爸回来了,这个过程就会触发一个事件,然后通知观察者(他妈)。

    image
    • 1)定义一个回调接口
    image
    • 2)定义妈妈类
    image
    • 3)定义小明类
    image
    • 4)测试
    image
    • 5)运行查看结果
    image

    三.Java中的观察者模式

    观察者模式

    在上图的结构,其中Observer作为观察者,Observable则作为被观察者,Observable的状态改变会给注册的Observer进行通知,考虑易用和低耦合,保证高度的协作。

    1.代码展示

    MyObservable非常简单,只是继承了Observable:

    public class MyObservable extends Observable {
    
        public static void main(String[] args) {
            //定义被观察者
            MyObservable myObservable = new MyObservable();
            //建立被观察者和观察者间的关系
            myObservable.addObserver(new MyObserver());
            //这里需要注意 让其触发事件时及时通知
            myObservable.setChanged();
            //触发事件,通知观察者
            myObservable.notifyObservers("helll");
        }
    }
    
    

    此处为了简化结构,直接将main方法写在MyObservable中用于进行效果的测试,main中我们先实例化了MyObservable类,并且将一个MyObserver实例用addObserver方法加入它的观察者列表中,然后通过setChanged改变changed的状态值,随后便可以notifyObservers,里面可以带参数做消息传递,也可以不带参数。关于changed,是因为只有该变量为true的情况下,才会进行通知,否则notifyObservers是无法通知到具体Observer的。

    MyObserver的结构也非常简单,由于继承了Observer接口,因此需要实现它的抽象方法update,这个方法也是用于接收Observable发来的通知并执行相应的处理逻辑的:

    public class MyObserver implements Observer{
    
        public void update(Observable o, Object arg) {
            System.out.println("观察对象为:" + o + ";传递参数为:" + arg);
        }
    }
    
    

    运行一下结果为:

    JAVA中对观察者模式的支持.png

    而在注释了setChanged方法之后,会发现没有任何输出,可以理解为,并没有真正通知到Observer。

    2.原理解析

    原理非常简单,查看Observable源码即可。其实从类图中就可以看出一些端倪,Observable中只有两个变量,一个是changed,一个是obs,从之前的分析可以看出changed只是一个状态指示的变量,那么obs应该就是一个用于存储Observer实例的变量了,它具体是用一个Vector来进行存储的。

    private boolean changed = false;
    private Vector<Observer> obs;
    
    

    这样的情况下,我们很容易就可以猜测,obs的初始化应该是在Observable的构造方法中实现的,果不其然,构造方法也仅仅只是完成了obs的初始化:

    public Observable() {
            obs = new Vector<>();
        }
    
    

    然后我们更为关心的是Observable与Observer之间的关系,那么就是重点关注两个方法,addObserver和notifyObservers。addObserver很显然,里面的主要操作是将作为参数传入的Observer加入到Vector中:

    public synchronized void addObserver(Observer o) {
            if (o == null)
                throw new NullPointerException();
            if (!obs.contains(o)) {
                obs.addElement(o);
            }
        }
    
    

    notifyObservers则是逐一通知Observer的过程,不过在这个方法中会检查changed的值,以及会在通知完成后对changed归零(其中clearChanged方法就是将changed变量重新置为false

    public void notifyObservers(Object arg) {
            Object[] arrLocal;
    
            synchronized (this) {
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }
    
    

    到这里基本上整个基于jdk实现的观察者模式的原理已经非常明了了,另外值得注意的一个点是,这个实现中用了一些措施来使它线程安全。比如,使用线程安全的容器Vector,addObserver方法进行了枷锁,在notifyObservers方法中,有一个操作进行了加锁

    synchronized (this) {
        if (!changed)
            return;
        arrLocal = obs.toArray();
        clearChanged();
    }
    
    

    这里主要确保了对changed变量的验证和清零操作以及将obs中的元素赋值给arrLocal的过程是原子操作,这里减少了锁的粒度,提高并发性,也因此大家能明白为何需要arrLocal这个变量了,随后对arrLocal数组进行便利并逐一通知则不需要加锁了,因为arrLocal已经是一个本地变量,并未暴露。

    总结

    1.使用场景
    一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

    一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

    一个对象必须通知其他对象,而并不知道这些对象是谁。

    需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

    2.注意事项

    • JAVA 中已经有了对观察者模式的支持类。
    • 避免循环引用。
    • 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

    参考:

    相关文章

      网友评论

        本文标题:【重温设计模式】JAVA中对观察者模式的支持

        本文链接:https://www.haomeiwen.com/subject/mymonqtx.html