谈谈java中的观察者模式

作者: 程序员Sunny | 来源:发表于2018-10-02 19:27 被阅读140次
    图片.png

    前言

    了解设计模式的童鞋应该都知道观察者模式,类似于上图的结构,其中Observer作为观察者,Observable则作为被观察者,Observable的状态改变会给注册的Observer进行通知,考虑易用和低耦合,保证高度的协作。

    Demo展示

    我们首先来看一下demo,也就是上图类图中所展示的程序,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("aaa");
    
        }
    }
    

    此处为了简化结构,直接将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);
    
        }
    }
    

    运行一下demo,可以看到结果为:

    观察对象为:com.sunny.MyObservable@60e53b93;传递参数为:aaa
    

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

    原理解析

    原理非常简单,查看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已经是一个本地变量,并未暴露。

    综上,就是基于jdk实现观察者模式的主要原理了。
    欢迎小伙伴与我讨论哦~

    邮箱:zsunny@yeah.net

    本文欢迎转载,请注明本文地址:https://www.jianshu.com/p/e45a12e3ac60

    相关文章

      网友评论

        本文标题:谈谈java中的观察者模式

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