调侃《HeadFirst设计模式》之观察者模式

来源:互联网 发布:王者荣耀刷点券软件 编辑:程序博客网 时间:2024/05/01 12:34

      上次总结完了策略模式,这次继续跟随着《Head First 设计模式》,聊下观察者模式。

    现在你接到了一个气象监测应用的项目,此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(获取来自气象站的数据,并更新布告板)和布告板(显示装置这只是其中一个,还有气象统计和天气预报布告板显示目前天气状况给用户看)。大概是下面这样的图:(右边的显示装置只是其中一个布告板,目前还有气象统计和天气预报

    

  WeatherData的源代码主要结构如下:


   WeatherData可以通过三个get方法获取到实际的天气数据。 一旦气象测量数据有更新,WeatherData的measurementsChanged()方法就会被调用。而一旦数据更新WeatherData就要立刻通知三个布告板。

    现在我们的任务是实现measurementsChanged(),让它更好的更新三个布告板显示的数据。要求是此系统必须有可扩展性,让其他人员简历定制的布告板,用户可以随心所欲添加删除布告板。而目前只有三个布告板。

    一个错误示范:

   

     里面的update方法类似于将新的temp,humidity,pressure赋值给三个布告板的成员变量。

    为什么说是错误的呢?

    1.它针对实现编程,没有针对接口,这样导致每次增加布告板都要修改里面的代码。

    2.可能改变的地方没有封装起来(三个更新方法直接在measurementsChanged()中)。

    3.这样根本不可能在运行时动态添加删除一些布告板。

    所以它的可扩展性非常差。


    对此,可以用观察者模式。那什么是观察者模式呢?

    观察者模式可以用订阅报纸来诠释。

    1报社的业务就是出版报纸。
    2向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。
    3当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。
    4只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。
向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。

   此时,出版社可以看做是观察者模式的主题,订阅者是观察者。可以用一张图的来解释:

   

     如图,老鼠、狗、猫对象已经订阅主题,所以主题有更新立刻通知它们,而鸭子没有订阅,主题的变化它什么也不知道。鸭子哪天想知道主题更新状态可以自己订阅主题,老鼠、狗、猫对象哪天不想知道主题动态了也可以取消订阅,一切自愿原则,而且主题和动物对象们互相不影响。、

     来个官方的定义:

    观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

     以下是最常用的实现观察者模式的示意图:

     

    观察者模式强调的是松耦合的原则:当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者。有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。

    松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

    根据以上叙述,可以将之前的任务设计如下图:

    

     实现气象站项目的代码:

    首先是接口:

    

      WeatherData的代码:

     

     通过多态,只需要实现同一个接口的类都可注册,所以可以用一个集合来注册和取消观察。当数据发生改变,就遍历集合的所有元素,调用元素的update方法。

     布告板代码:

     

       这样在运行中,我们可以new出一个布告板对象(比如CurrentConditionsDisplay),将一个WeatherData对象作为参数传入,这样在构造上方法中就立刻将CurrentConditionsDisplay对象注册到这个WeatherData对象中。

        测试程序可以这样写:

        

0 0