`
scanfprintf123
  • 浏览: 79245 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

阅读更多

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



 

 

    从上图可以看出,具体的观察者对具体的主题有一个依赖, 而且从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模式,可以表示为:

    

 

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

  

public class MessagesSubject extends AbstractSubject implements Runnable
{

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

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

 

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

 

 

 

  • 大小: 15.5 KB
  • 大小: 3.9 KB
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics