关于Observer模式不得不说的二三事

系统 1095 0

   今天有同事问我关于Observer模式的一个问题,说观察者(Observer)为什么要依赖于主题(Subject),如下图所示:


关于Observer模式不得不说的二三事
 

 

    从上图可以看出,具体的观察者对具体的主题有一个依赖, 而且从JDK本身提供的Observer接口,我们也可以看到,确实对主题(在JDK的实现中,为Observable接口)有一个依赖,如代码所示:

 

    public interface Observer {
    void update(Observable o, Object arg);
}
  

  

 

   这个是为什么呢,为什么这里会依赖主题对象?其实这个跟Subject与Observer之间通讯的方式有关,当Subject本身状态发生变化时,会去通知注册了的Observer(即调用每个observer的update()方法),那么这个时候,主题本身要怎么样去给Observer传输其需要的数据呢?

  

   在我们看到的大多数观察者的实现中,主题(Subject)在通知注册的Observer时,都会把Observer所需要的数据封装好,传给Observer,这个也就是所谓的‘推’的模式,主题主动将数据推给观察者,这种情况下,Observer的接口往往定义如下:

      

    public interface Observer {
    void update(Object arg);//从主题传入的数据
}

  

 

   在这种‘推’的模式下,观察者本身是不依赖于主题对象的。 但还有另外一种所谓‘拉’的通讯方式,是指观察者在需要数据的时候主动从主题对象中获取,这个情况下面就会出现观察者依赖于主题对象,

   

    public interface Observer {
    void update(Subject subject); 
}
  

 

    由于这种拉的实现方法出现的比较少,而且‘拉’数据的模式有一个比较大的缺点,那就是出现了主题对象和观察者对象之间的循环依赖,处理不好则很容易出来死循环。

   

     但是对于一个完整的观察者模式来说,这两种数据传输的方式都是需要的,这也就是JDK的Observer接口中的update()方法要有两个参数的原因(Obervable对象一般对应于拉模式,Object对象一般对应于推模式),如果你做过Swing编程,你会发现在Swing的事件处理中,listner(实际上就是Observer)所接受的参数也支持推拉两种数据方式,如

     

    public interface MouseListener extends EventListener {
    public void mouseClicked(MouseEvent e);

    。。。其他略
}
  

    这里的MouseEvent对象实际上也包含了数据来源对象(触发事件的对象),即具体主题对象,而除了主题对象之外的其他属性,我们都可以看成是推模式中所传的数据。

 

     好了,解决了同事了疑问,还需要点明Observer模式的另一职责。由于我们大多数的Observer模式的实现都很简单,在这样的实现下,主题对象大多只拥有一个职责,那就是管理Observer的职责(包括通知Observer),

    

    class ConcreteSubject implement ISubject
{
    private List observers=..
    public void addObserver(Observer obs)
    {
         //add observer
    }
    
    public void removeObserver(Observer obs)
    {
         //remove observer
    }

    public void notifyObservers(Object obj)
    {
        //notify observers
    }

}
  

 

    加上Observer模式是为了解决一对多的关系,久而久之,导致大多数人都忘记了主题对象(Subject)本身还应该有另一个职责,管理Observer只是主题(Subject)对象应有的共同的职责,不要忘了,还有多主题对象这么一回事。举个以前看到的例子,

    假设我们的主题对象需要从远程获取一些数据,并分别的将其记录在DB中,和显示在Screen上,那么套用Observer模式,可以表示为:

    
关于Observer模式不得不说的二三事
 

   其中,DBObserver将拿到的数据写到DB中,而DisplayObserver将拿到的数据显示在SCREEN上,而MessagesSubject则有了两个职责,一个是我们前面说的管理Observer的职责,另一个是去远程取数据的职责,而这个我认为才是主题对象(Subject)应该有的具体的职责。

  

    public class MessagesSubject extends AbstractSubject implements Runnable
{

//管理Observer的职责会从AbstractSuject中获得

//真正的业务逻辑
public void run()
{
    //从远程获得messages
    //通知观察者
}
  

 

    完成一个完整的Observer模式很难,考虑的东西比较多(光是通知Observer这部分就有几种不同的实现方式),不推荐每次都需要实现一个很完整的Observer模式,但是我们不应该遗忘这些构成完整Observer模式锁需要的部分。

 

 

 

关于Observer模式不得不说的二三事


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论