程序员架构算法设计模式和编程理论

观察者模式

2017-06-28  本文已影响0人  holly_wang_王小飞

观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态发生改变时,需要通知相应的观察者,使这些观察者对象能够自动更新。

主题

主题是观察者观察的对象,三个特征。

观察者

当主题发生变化,收到通知进行具体的处理

模式优势

这里举一个例子来说明,牛奶送奶站就是主题,订奶客户为监听者,客户从送奶站订阅牛奶后,会每天收到牛奶。如果客户不想订阅了,可以取消,以后就不会收到牛奶。

松耦合

通知不错过

由于被动接受,正常情况下不会错过主题的改变通知。而主动获取的话,由于时机选取问题,可能导致错过某些状态。

Java实现

Java中有观察者模式使用的API

package strategy.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * @createholly.wang 2017-06-28 9:41
 * @email wangxfholly@126.com
 */
public class MainRoot {
    public static void main(String[] args) {
        Observer consumer = new Consumer();
        MilkProvider provider = new MilkProvider();
        provider.addObserver(consumer);
        provider.milkProduced();
    }
    static class MilkProvider extends Observable {
            public void  milkProduced() {
                setChanged();//状态改变必须调用
                notifyObservers();
            }
    }
    static class Consumer implements Observer {

        @Override
        public void update(Observable o, Object arg) {
            System.out.println("Consumer update..." + o + ";arg=" + arg);
        }
    }
}

setChanged

 private boolean changed = false;
 protected synchronized void setChanged() {
        changed = true;
    }

setChanged 是加入了一个开关 changed,在notifyObservers 方法中会进行判断

public void notifyObservers() {
        notifyObservers(null);
    }
public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

加入开关可能有

  1. 筛选有效通知,只有有效通知可以调用setChanged。比如,我的微信朋友圈一条状态,好友A点赞,后续该状态的点赞和评论并不是每条都通知A,只有A的好友触发的操作才会通知A。
  2. 便于撤销通知操作,在主题中,我们可以设置很多次setChanged,但是在最后由于某种原因需要取消通知,我们可以使用clearChanged轻松解决问题。
  3. 主动权控制,由于setChanged为protected,而notifyObservers方法为public,这就导致存在外部随意调用notifyObservers的可能,但是外部无法调用setChanged,因此真正的控制权应该在主题这里。

主动获取

观察者模式即所谓的推送方式,然而推送并非完美无缺。比如主题变化会推送大量的数据,而其中的一些观察者只需要某项数据,此时观察者就需要在具体实现中花费时间筛选数据。

这确实是个问题,想要解决也不难,需要主题为某些数据提供getter方法,观察者只需调用getter取数据处理即可。

package strategy.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * ${DESCRIPTION}
 *
 * @createholly.wang 2017-06-28 9:41
 * @email wangxfholly@126.com
 */
public class MainRoot {
    public static void main(String[] args) {
        Observer consumer = new Consumer();
        MilkProvider provider = new MilkProvider();
        provider.addObserver(consumer);
        provider.milkProduced();
    }
    static class MilkProvider extends Observable {
        public void  milkProduced() {
            setChanged();//状态改变必须调用
            notifyObservers();
        }
        public float getPrice() {
            return 2.5f;
        }
    }
    static class Consumer implements Observer {

        @Override
        public void update(Observable o, Object arg) {
            MilkProvider provider = (MilkProvider)o;
            System.out.println("milk price =" + provider.getPrice());
        }
    }
}

不足与隐患

主要的问题表现在内存管理上,主要由以下两点

推拉互补

大多数系统避免不足,都是采用推拉模式 ,即推送关键消息,然后提供接口给观察者主动来调用获取详情。事务补偿机制主要采用这种方法,分布式一致性事务的解决办法也是如此,好多消息系统,微服务大多采用这种方法。

上一篇 下一篇

猜你喜欢

热点阅读