`
chjavach
  • 浏览: 460924 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

研磨设计模式 之 观察者模式(Observer) 3——跟着cc学设计系列

 
阅读更多

12.3  模式讲解

12.3.1  认识观察者模式

(1)目标和观察者之间的关系

       按照模式的定义,目标和观察者之间是典型的一对多的关系。

       但是要注意,如果观察者只有一个,也是可以的,这样就变相实现了目标和观察者之间一对一的关系,这也使得在处理一个对象的状态变化会影响到另一个对象的时候,也可以考虑使用观察者模式。

       同样的,一个观察者也可以观察多个目标,如果观察者为多个目标定义的通知更新方法都是update方法的话,这会带来麻烦,因为需要接收多个目标的通知,如果是一个update的方法,那就需要在方法内部区分,到底这个更新的通知来自于哪一个目标,不同的目标有不同的后续操作。

       一般情况下,观察者应该为不同的观察者目标,定义不同的回调方法,这样实现最简单,不需要在update方法内部进行区分。

(2)单向依赖

       在观察者模式中,观察者和目标是单向依赖的,只有观察者依赖于目标,而目标是不会依赖于观察者的。

       它们之间联系的主动权掌握在目标手中,只有目标知道什么时候需要通知观察者,在整个过程中,观察者始终是被动的,被动的等待目标的通知,等待目标传值给它。

       对目标而言,所有的观察者都是一样的,目标会一视同仁的对待。当然也可以通过在目标里面进行控制,实现有区别对待观察者,比如某些状态变化,只需要通知部分观察者,但那是属于稍微变形的用法了,不属于标准的、原始的观察者模式了。

(3)基本的实现说明

  • 具体的目标实现对象要能维护观察者的注册信息,最简单的实现方案就如同前面的例子那样,采用一个集合来保存观察者的注册信息。
  • 具体的目标实现对象需要维护引起通知的状态,一般情况下是目标自身的状态,变形使用的情况下,也可以是别的对象的状态。
  • 具体的观察者实现对象需要能接收目标的通知,能够接收目标传递的数据,或者是能够主动去获取目标的数据,并进行后续处理。
  • 如果是一个观察者观察多个目标,那么在观察者的更新方法里面,需要去判断是来自哪一个目标的通知。一种简单的解决方案就是扩展update方法,比如在方法里面多传递一个参数进行区分等;还有一种更简单的方法,那就是干脆定义不同的回调方法。

(4)命名建议

  • 观察者模式又被称为发布-订阅模式
  • 目标接口的定义,建议在名称后面跟Subject
  • 观察者接口的定义,建议在名称后面跟Observer
  • 观察者接口的更新方法,建议名称为update,当然方法的参数可以根据需要定义,参数个数不限、参数类型不限

(5)触发通知的时机

    在实现观察者模式的时候,一定要注意触发通知的时机,一般情况下,是在完成了状态维护后触发,因为通知会传递数据,不能够先通知后改数据,这很容易出问题,会导致观察者和目标对象的状态不一致。比如:目标一发出通知,就有观察者来取值,结果目标还没有更新数据,这就明显造成了错误。如下示例就是有问题的了,示例代码如下:

public void setContent(String content) {

//一激动,目标先发出通知了,然后才修改自己的数据,这会造成问题

    notifyAllReader();  

    this.content = content;

}

(6)相互观察

    在某些应用里面,可能会出现目标和观察者相互观察的情况。什么意思呢,比如有两套观察者模式的应用,其中一套观察者模式的实现是A对象、B对象观察C对象;在另一套观察者模式的实现里面,实现的是B对象、C对象观察A对象,那么A对象和C对象就是在相互观察。

    换句话说,A对象的状态变化会引起C对象的联动操作,反过来,C 对象的状态变化也会引起A对象的联动操作。对于出现这种状况,要特别小心处理,因为可能会出现死循环的情况。

(7)观察者模式的调用顺序示意图

在使用观察者模式时,会很明显的分成两个阶段,第一个阶段是准备阶段,也就是维护目标和观察者关系的阶段,这个阶段的调用顺序如图12.5所示:

 

图12.5  观察者模式准备阶段示意图

    接下来就是实际的运行阶段了,这个阶段的调用顺序如图12.6所示:

 

图12.6  观察者模式运行阶段示意图

(8)通知的顺序

    从理论上说,当目标对象的状态变化后通知所有观察者的时候,顺序是不确定的,因此观察者实现的功能,绝对不要依赖于通知的顺序,也就是说,多个观察者之间的功能是平行的,相互不应该有先后的依赖关系。

12.3.2  推模型和拉模型

       在观察者模式的实现里面,又分为推模型和拉模型两种方式,什么意思呢?

  • 推模型
        目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据,相当于是在广播通信。
  • 拉模型
        目标对象在通知观察者的时候,只传递少量信息,如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。
        一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

根据上面的描述,发现前面的例子就是典型的拉模型,那么推模型如何实现呢,还是来看个示例吧,这样会比较清楚。

(1)推模型的观察者接口

       根据前面的讲述,推模型通常都是把需要传递的数据直接推送给观察者对象,所以观察者接口中的update方法的参数需要发生变化,示例代码如下:

/**

 * 观察者,比如报纸的读者

 */

public interface Observer {

    /**

     * 被通知的方法,直接把报纸的内容推送过来

     * @param content 报纸的内容

     */

    public void update(String content);

}

(2)推模型的观察者的具体实现

       以前需要到目标对象里面获取自己需要的数据,现在是直接接收传入的数据,这就是改变的地方,示例代码如下:

public class Reader implements Observer{

    /**

     * 读者的姓名

     */

    private String name;

 

    public void update(String content) {

       //这是采用推的方式

       System.out.println(name+"收到报纸了,阅读先。内容是==="

+content);

    }

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

}

(3)推模型的目标对象

       跟拉模型的目标实现相比,有一些变化:

  • 一个就是通知所有观察者的方法,以前是没有参数的,现在需要传入需要主动推送的数据
  • 另外一个就是在循环通知观察者的时候,也就是循环调用观察者的update方法的时候,传入的参数不同了

示例代码如下:

/**

 * 目标对象,作为被观察者,使用推模型

 */

public class Subject {

    /**

     * 用来保存注册的观察者对象,也就是报纸的订阅者

     */

    private List<Observer> readers = new ArrayList<Observer>();

    /**

     * 报纸的读者需要先向报社订阅,先要注册

     * @param reader 报纸的读者

     * @return 是否注册成功

     */

    public void attach(Observer reader) {

       readers.add(reader);

    }

    /**

     * 报纸的读者可以取消订阅

     * @param reader 报纸的读者

     * @return 是否取消成功

     */

public void detach(Observer reader) {

       readers.remove(reader);

    }

    /**

     * 当每期报纸印刷出来后,就要迅速的主动的被送到读者的手中,

     * 相当于通知读者,让他们知道

     * @param content 要主动推送的内容

     */

    protected void notifyObservers(String content) {

       for(Observer reader : readers){

           reader.update(content);

       }

    }

}

(4)推模型的目标具体实现

       跟拉模型相比,有一点变化,就是在调用通知观察者的方法的时候,需要传入参数了,拉模型的实现中是不需要的,示例代码如下:

public class NewsPaper extends Subject{

    private String content;

    public String getContent() {

       return content;

    }

    public void setContent(String content) {

       this.content = content;

       //内容有了,说明又出报纸了,那就通知所有的读者

       notifyObservers(content);

    }

}

(5)推模型的客户端使用

       跟拉模型一样,没有变化。

好了,到此就简单的实现了拉模型的观察者模式,去测试一下,看看效果,是不是和前面的推模型一样呢?如果是一样的,那就对了。

(6)关于两种模型的比较

       两种实现模型,在开发的时候,究竟应该使用哪一种,还是应该具体问题具体分析。这里,只是把两种模型进行一个简单的比较。

  • 推模型是假定目标对象知道观察者需要的数据;而拉模型是目标对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传给观察者,让观察者自己去按需取值。
  • 推模型可能会使得观察者对象难以复用,因为观察者定义的update方法是按需而定义的,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能需要提供新的update方法,或者是干脆重新实现观察者。
        而拉模型就不会造成这样的情况,因为拉模型下,update方法的参数是目标对象本身,这基本上是目标对象能传递的最大数据集合了,基本上可以适应各种情况的需要。

12.3.3  Java中的观察者模式

       估计有些朋友在看前面的内容的时候,心里就嘀咕上了,Java里面不是已经有了观察者模式的部分实现吗,为何还要全部自己从头做呢?

       主要是为了让大家更好的理解观察者模式本身,而不用受Java语言实现的限制。

       好了,下面就来看看如何利用Java中已有的功能来实现观察者模式。在java.util包里面有一个类Observable,它实现了大部分我们需要的目标的功能;还有一个接口Observer,它里面定义了update的方法,就是观察者的接口。

       因此,利用Java中已有的功能来实现观察者模式非常简单,跟前面完全由自己来实现观察者模式相比有如下改变:

  • 不需要再定义观察者和目标的接口了,JDK帮忙定义了
  • 具体的目标实现里面不需要再维护观察者的注册信息了,这个在Java中的Observable类里面,已经帮忙实现好了
  • 触发通知的方式有一点变化,要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能
  • 具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个是Java在定义的时候,就已经考虑进去了

好了,说了这么多,还是看看例子会比较直观。

(1)新的目标的实现,不再需要自己来实现Subject定义,在具体实现的时候,也不是继承Subject了,而是改成继承Java中定义的Observable,示例代码如下:

/**

 * 报纸对象,具体的目标实现

 */

public class NewsPaper extends  java.util.Observable   {

    /**

     * 报纸的具体内容

     */

    private String content;

    /**

     * 获取报纸的具体内容

     * @return 报纸的具体内容

     */

    public String getContent() {

       return content;

    }

    /**

     * 示意,设置报纸的具体内容,相当于要出版报纸了

     * @param content 报纸的具体内容

     */

    public void setContent(String content) {

       this.content = content;

       //内容有了,说明又出新报纸了,那就通知所有的读者

       //注意在用Java中的Observer模式的时候,下面这句话不可少

       this.setChanged();

       //然后主动通知,这里用的是推的方式

       this.notifyObservers(this.content);

       //如果用拉的方式,这么调用

       //this.notifyObservers();

    }

}

(2)再看看新的观察者的实现,不是实现自己定义的观察者接口,而是实现由Java提供的Observer接口,示例代码如下:

/**

 * 真正的读者,为了简单就描述一下姓名

 */

public class Reader implements   java.util.Observer   {

/**

     * 读者的姓名

     */

    private String name;

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

public void update(Observable o, Object obj) {

       //这是采用推的方式

       System.out.println(name

+"收到报纸了,阅读先。目标推过来的内容是==="+obj);

 
   

 

 

 


 

 

//这是获取拉的数据

System.out.println(name

+"收到报纸了,阅读先。主动到目标对象去拉的内容是==="

+((NewsPaper)o).getContent());

 

 

 

 

 

 

 


    }

}

(3)客户端使用

客户端跟前面的写法没有太大改变,主要在注册阅读者的时候,调用的方法跟以前不一样了,示例代码如下:

public class Client {

    public static void main(String[] args) {

       //创建一个报纸,作为被观察者

       NewsPaper subject = new NewsPaper();

       //创建阅读者,也就是观察者

       Reader reader1 = new Reader();

       reader1.setName("张三");

 

       Reader reader2 = new Reader();

       reader2.setName("李四");

 

       Reader reader3 = new Reader();

       reader3.setName("王五");

 

       //注册阅读者

       subject.addObserver(reader1);

       subject.addObserver(reader2);

       subject.addObserver(reader3);

 

       //要出报纸啦

       subject.setContent("本期内容是观察者模式");

    }

}

赶紧测试一下,运行运行,看看结果,运行结果如下所示:

王五收到报纸了,阅读先。目标推过来的内容是===本期内容是观察者模式

王五收到报纸了,阅读先。主动到目标对象去拉的内容是===本期内容是观察者模式

李四收到报纸了,阅读先。目标推过来的内容是===本期内容是观察者模式

李四收到报纸了,阅读先。主动到目标对象去拉的内容是===本期内容是观察者模式

张三收到报纸了,阅读先。目标推过来的内容是===本期内容是观察者模式

张三收到报纸了,阅读先。主动到目标对象去拉的内容是===本期内容是观察者模式

然后好好对比自己实现观察者模式和使用Java已有的功能来实现观察者模式,看看有什么不同,有什么相同,好好体会一下。

12.3.4  观察者模式的优缺点

l          观察者模式实现了观察者和目标之间的抽象耦合
    原本目标对象在状态发生改变的时候,需要直接调用所有的观察者对象,但是抽象出观察者接口过后,目标和观察者就只是在抽象层面上耦合了,也就是说目标只是知道观察者接口,并不知道具体的观察者的类,从而实现目标类和具体的观察者类之间解耦。

l          观察者模式实现了动态联动
    所谓联动,就是做一个操作会引起其它相关的操作。由于观察者模式对观察者注册实行管理,那就可以在运行期间,通过动态的控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。

l          观察者模式支持广播通信
    由于目标发送通知给观察者是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行广播。当然,也可以通过在目标上添加新的功能来限制广播的范围。
    在广播通信的时候要注意一个问题,就是相互广播造成死循环的问题。比如A和B两个对象互为观察者和目标对象,A对象发生状态变化,然后A来广播信息,B对象接收到通知后,在处理过程中,使得B对象的状态也发生了改变,然后B来广播信息,然后A对象接到通知后,又触发广播信息……,如此A引起B变化,B又引起A变化,从而一直相互广播信息,就造成死循环了。

l          观察者模式可能会引起无谓的操作
    由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法,如果观察者不需要执行相应处理,那么这次操作就浪费了。
    其实浪费了还好,怕就怕引起了误更新,那就麻烦了,比如:本应该在执行这次状态更新前把某个观察者删除掉,这样通知的时候就没有这个观察者了,但是现在忘掉了,那么就会引起误操作。

12.3.5  思考观察者模式

1:观察者模式的本质

观察者模式的本质:触发联动

       当修改目标对象的状态的时候,就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法,其实就相当于联动调用这些观察者的方法。

       而且这个联动还是动态的,可以通过注册和取消注册来控制观察者,因而可以在程序运行期间,通过动态的控制观察者,来变相的实现添加和删除某些功能处理,这些功能就是观察者在update的时候执行的功能。

同时目标对象和观察者对象的解耦,又保证了无论观察者发生怎样的变化,目标对象总是能够正确地联动过来。

       理解这个本质对我们非常有用,对于我们识别和使用观察者模式有非常重要的意义,尤其是在变形使用的时候,万变不离其宗。

2:何时选用观察者模式

建议在如下情况中,选用观察者模式:

  • 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,那么就可以选用观察者模式,将这两者封装成观察者和目标对象,当目标对象变化的时候,依赖于它的观察者对象也会发生相应的变化。这样就把抽象模型的这两个方面分离开了,使得它们可以独立的改变和复用。
  • 如果在更改一个对象的时候,需要同时连带改变其它的对象,而且不知道究竟应该有多少对象需要被连带改变,这种情况可以选用观察者模式,被更改的那一个对象很明显就相当于是目标对象,而需要连带修改的多个其它对象,就作为多个观察者对象了。
  • 当一个对象必须通知其它的对象,但是你又希望这个对象和其它被它通知的对象是松散耦合的,也就是说这个对象其实不想知道具体被通知的对象,这种情况可以选用观察者模式,这个对象就相当于是目标对象,而被它通知的对象就是观察者对象了。

12.3.6  Swing中的观察者模式

       Java的Swing中到处都是观察者模式的身影,比如大家熟悉的事件处理,就是典型的观察者模式的应用。(说明一下:早期的Swing事件处理用的是职责链)

       Swing组件是被观察的目标,而每个实现监听器的类就是观察者,监听器的接口就是观察者的接口,在调用addXXXListener方法的时候就相当于注册观察者。

当组件被点击,状态发生改变的时候,就会产生相应的通知,会调用注册的观察者的方法,就是我们所实现的监听器的方法。

从这里还可以学一招:如何处理一个观察者观察多个目标对象?

你看一个Swing的应用程序,作为一个观察者,经常会注册观察多个不同的目标对象,也就是同一类,既实现了按钮组件的事件处理,又实现了文本框组件的事件处理,是怎么做到的呢?

答案就在监听器接口上,这些监听器接口就相当于观察者接口,也就是说一个观察者要观察多个目标对象,只要不同的目标对象使用不同的观察者接口就好了,当然,这些接口里面的方法也不相同,不再都是update方法了。这样一来,不同的目标对象通知观察者所调用的方法也就不同了,这样在具体实现观察者的时候,也就实现成不同的方法,自然就区分开了。

12.3.7  简单变形示例——区别对待观察者

       首先声明,这里只是举一个非常简单的变形使用的例子,也可算是基本的观察者模式的功能加强,事实上可以有很多很多的变形应用,这也是为什么我们特别强调大家要深入理解每个设计模式,要把握每个模式的本质的原因了。

       1:范例需求

       这是一个实际系统的简化需求:在一个水质监测系统中有这样一个功能,当水中的杂质为正常的时候,只是通知监测人员做记录;当为轻度污染的时候,除了通知监测人员做记录外,还要通知预警人员,判断是否需要预警;当为中度或者高度污染的时候,除了通知监测人员做记录外,还要通知预警人员,判断是否需要预警,同时还要通知监测部门领导做相应的处理。

       2:解决思路和范例代码

       分析上述需求就会发现,对于水质污染这件事情,有可能会涉及到监测员、预警人员、监测部门领导,根据不同的水质污染情况涉及到不同的人员,也就是说,监测员、预警人员、监测部门领导他们三者是平行的,职责都是处理水质污染,但是处理的范围不一样。

       因此很容易套用上观察者模式,如果把水质污染的记录当作被观察的目标的话,那么监测员、预警人员和监测部门领导就都是观察者了。

       前面学过的观察者模式,当目标通知观察者的时候是全部都通知,但是现在这个需求是不同的情况来让不同的人处理,怎么办呢?

       解决的方式通常有两种,一种是目标可以通知,但是观察者不做任何操作;另外一种是在目标里面进行判断,干脆就不通知了。两种实现方式各有千秋,这里选择后面一种方式来示例,这种方式能够统一逻辑控制,并进行观察者的统一分派,有利于业务控制和今后的扩展。

       还是看代码吧,会更直观。

(1)先来定义观察者的接口,这个接口跟前面的示例差别也不大,只是新加了访问观察人员职务的方法,示例代码如下:

/**

 * 水质观察者接口定义

 */

public interface WatcherObserver {

    /**

     * 被通知的方法

     * @param subject 传入被观察的目标对象

     */

    public void update(WaterQualitySubject subject);

    /**

     * 设置观察人员的职务

     * @param job 观察人员的职务

     */

    public void setJob(String job);

    /**

     * 获取观察人员的职务

     * @return 观察人员的职务

     */

    public String getJob();

}

(2)定义完接口后,来看看观察者的具体实现,示例代码如下:

/**

 * 具体的观察者实现

 */

public class Watcher implements WatcherObserver{

    /**

     * 职务

     */

    private String job;

public String getJob() {

       return this.job;

    }  

    public void setJob(String job) {

       this.job = job;

    }

 

    public void update(WaterQualitySubject subject) {

       //这里采用的是拉的方式

       System.out.println(job+"获取到通知,当前污染级别为:"

+subject.getPolluteLevel());

    }

}

(3)接下来定义目标的父对象,跟以前相比有些改变:

  • 把父类实现成抽象的,因为在里面要定义抽象的方法
  • 原来通知所有的观察者的方法被去掉了,这个方法现在需要由子类去实现,要按照业务来有区别的来对待观察者,得看看是否需要通知观察者
  • 新添加一个水质污染级别的业务方法,这样在观察者获取目标对象的数据的时候,就不需要再知道具体的目标对象,也不需要强制造型了

示例代码如下:

/**

 * 定义水质监测的目标对象

 */

public abstract class WaterQualitySubject {

    /**

     * 用来保存注册的观察者对象

     */

    protected List<WatcherObserver> observers =

new ArrayList<WatcherObserver>();

    /**

     * 注册观察者对象

     * @param observer 观察者对象

     */

    public void attach(WatcherObserver observer) {

       observers.add(observer);

    }

    /**

     * 删除观察者对象

     * @param observer 观察者对象

     */

    public void detach(WatcherObserver observer) {

       observers.remove(observer);

    }

    /**

     * 通知相应的观察者对象

     */

    public abstract void notifyWatchers();

    /**

     * 获取水质污染的级别

     * @return 水质污染的级别

     */

    public abstract int getPolluteLevel();

}

(4)接下来重点看看目标的实现,在目标对象里面,添加一个描述污染级别的属性,在判断是否需要通知观察者的时候,不同的污染程度对应会通知不同的观察者,示例代码如下:

/**

 * 具体的水质监测对象

 */

public class WaterQuality extends WaterQualitySubject{

    /**

     * 污染的级别,0表示正常,1表示轻度污染,2表示中度污染,3表示高度污染

     */

    private int polluteLevel = 0;

    /**

     * 获取水质污染的级别

     * @return 水质污染的级别

     */

    public int getPolluteLevel() {

       return polluteLevel;

    }

    /**

     * 当监测水质情况后,设置水质污染的级别

     * @param polluteLevel 水质污染的级别

     */

    public void setPolluteLevel(int polluteLevel) {

       this.polluteLevel = polluteLevel;

       //通知相应的观察者

       this.notifyWatchers();

    }

    /**

     * 通知相应的观察者对象

     */

    public void notifyWatchers() {

       //循环所有注册的观察者

       for(WatcherObserver watcher : observers){

                     //开始根据污染级别判断是否需要通知,由这里总控

                     if(this.polluteLevel >= 0){

                         //通知监测员做记录

                         if("监测人员".equals(watcher.getJob())){

                            watcher.update(this);

                         }

                     }

                     if(this.polluteLevel >= 1){

                         //通知预警人员

                         if("预警人员".equals(watcher.getJob())){

                            watcher.update(this);

                         }

                      }

                     if(this.polluteLevel >= 2){

                         //通知监测部门领导

                         if("监测部门领导".equals(

watcher.getJob())){

                            watcher.update(this);

                         }

                     }

       }

    }

}

(5)大功告成,来写个客户端,测试一下,示例代码如下:

public class Client {

    public static void main(String[] args) {

       //创建水质主题对象

       WaterQuality subject = new WaterQuality();

       //创建几个观察者

       WatcherObserver watcher1 = new Watcher();

       watcher1.setJob("监测人员");

       WatcherObserver watcher2 = new Watcher();

       watcher2.setJob("预警人员");

       WatcherObserver watcher3 = new Watcher();

       watcher3.setJob("监测部门领导");

 

       //注册观察者

       subject.attach(watcher1);

       subject.attach(watcher2);

       subject.attach(watcher3);

 

       //填写水质报告

       System.out.println("当水质为正常的时候------------------〉");

       subject.setPolluteLevel(0);

        System.out.println("当水质为轻度污染的时候---------------〉");

       subject.setPolluteLevel(1);

       System.out.println("当水质为中度污染的时候---------------〉");

       subject.setPolluteLevel(2);

    }

}

(6)运行一下,看看结果,如下:

当水质为正常的时候------------------〉

监测人员获取到通知,当前污染级别为:0

当水质为轻度污染的时候---------------〉

监测人员获取到通知,当前污染级别为:1

预警人员获取到通知,当前污染级别为:1

当水质为中度污染的时候---------------〉

监测人员获取到通知,当前污染级别为:2

预警人员获取到通知,当前污染级别为:2

监测部门领导获取到通知,当前污染级别为:2

       仔细观察上面输出的结果,你会发现,当填写不同的污染级别时,被通知的人员是不同的。但是这些观察者是不知道这些不同的,观察者只是在自己获得通知的时候去执行自己的工作。具体要不要通知,什么时候通知都是目标对象的工作。      

12.3.8  相关模式

l          观察者模式和状态模式
    观察者模式和状态模式是有相似之处的。
    观察者模式是当目标状态发生改变时,触发并通知观察者,让观察者去执行相应的操作。而状态模式是根据不同的状态,选择不同的实现,这个实现类的主要功能就是针对状态的相应的操作,它不像观察者,观察者本身还有很多其它的功能,接收通知并执行相应处理只是观察者的部分功能。
    当然观察者模式和状态模式是可以结合使用的。观察者模式的重心在触发联动,但是到底决定哪些观察者会被联动,这时就可以采用状态模式来实现了,也可以采用策略模式来进行选择需要联动的观察者。

l          观察者模式和中介者模式
    观察者模式和中介者模式是可以结合使用的。
    前面的例子中目标都只是简单的通知一下,然后让各个观察者自己去完成更新就结束了。如果观察者和被观察的目标之间的交互关系很复杂,比如:有一个界面,里面有三个下拉列表组件,分别是选择国家、省份/州、具体的城市,很明显这是一个三级联动,当你选择一个国家的时候,省份/州应该相应改变数据,省份/州一改变,具体的城市也需要改变。
    这种情况下,很明显需要相关的状态都联动准备好了,然后再一次性的通知观察者,就是界面做更新处理,不会国家改变一下,省份和城市还没有改,就通知界面更新。这种情况就可以使用中介者模式来封装观察者和目标的关系。
    在使用Swing的小型应用里面,也可以使用中介者模式。比如:把一个界面所有的事件用一个对象来处理,把一个组件触发事件过后,需要操作其它组件的动作都封装到一起,这个对象就是典型的中介者。

6
11
分享到:
评论

相关推荐

    研磨设计模式(完整带书签).part2.pdf

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式(完整带书签).part1.pdf

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式-part3

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式.part3(共4个)

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式-part2

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式-part4

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式.part2(共4个)

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式.part4(共4个)

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    研磨设计模式.part1(共4个)

    第12章 观察者模式(Observer) 第13章 命令模式(Command) 第14章 迭代器模式(Iterator) 第15章 组合模式(Composite) 第16章 模板方法模式(Template Method) 第17章 策略模式(Strategy) 第18章 状态模式...

    DesignPattern

    《研磨设计模式》书中的例子 --第二章:简单工厂 本质:选择实现 --第三章:外观模式(Facade) 本质:封装交互,简化调用 --第四章:适配器模式(Adapter) 本质:转换匹配,复用功能 --第五章:单例模式(Singleton) ...

Global site tag (gtag.js) - Google Analytics