收藏私塾在线
 

欢迎您来到私塾在线网!   

请登录! 

免费注册 


javasscc的笔记
状态: 离线
人气:490998
访问用户量:1124
笔记经验:1858
总积分:1958
级别:普通会员
搜索本笔记
ta的交流分类
ta的交流主题贴(196)
ta的所有交流贴(200)
ta的全部笔记
全部笔记(98)
未分类笔记(0)
研磨设计模式(94)
课程问题(0)
设计模式综合项目实战(4)
存档
2013-05(4)
2012-08(39)
2012-07(55)

2013-01-18 18:49:24
研磨设计模式 之 观察者模式(Observer) 2——跟着cc学设计系列
浏览(4187)|评论(1)   交流分类:Java|笔记分类: 研磨设计模式

12.2  解决方案

12.2.1  观察者模式来解决

用来解决上述问题的一个合理的解决方案就是观察者模式。那么什么是观察者模式呢?

(1)观察者模式定义

 

(2)应用观察者模式来解决的思路

在前面描述的订阅报纸的例子里面,对于报社来说,在一开始,它并不清楚究竟有多少个订阅者会来订阅报纸,因此,报社需要维护一个订阅者的列表,这样当报社出版报纸的时候,才能够把报纸发放到所有的订阅者手中。对于订阅者来说,订阅者也就是看报的读者,多个订阅者会订阅同一份报纸。

这就出现了一个典型的一对多的对象关系,一个报纸对象,会有多个订阅者对象来订阅;当报纸出版的时候,也就是报纸对象改变的时候,需要通知所有的订阅者对象。那么怎么来建立并维护这样的关系呢?

观察者模式可以处理这种问题,观察者模式把这多个订阅者称为观察者:Observer,多个观察者观察的对象被称为目标:Subject。

一个目标可以有任意多个观察者对象,一旦目标的状态发生了改变,所有注册的观察者都会得到通知,然后各个观察者会对通知作出相应的响应,执行相应的业务功能处理,并使自己的状态和目标对象的状态保持一致。

12.2.2  模式结构和说明

观察者模式结构如图12.3所示:

 

图12.3  观察者模式结构示意图

Subject

目标对象,通常具有如下功能:

  • 一个目标可以被多个观察者观察
  • 目标提供对观察者注册和退订的维护
  • 当目标的状态发生变化时,目标负责通知所有注册的、有效的观察者

Observer

       定义观察者的接口,提供目标通知时对应的更新方法,这个更新方法进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据。

ConcreteSubject

       具体的目标实现对象,用来维护目标状态,当目标对象的状态发生改变时,通知所有注册有效的观察者,让观察者执行相应的处理。

ConcreteObserver

       观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新自身的状态以保持和目标的相应状态一致。

12.2.3  观察者模式示例代码

(1)先来看看目标对象的定义,示例代码如下:

/**

 * 目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口

 */

public class Subject {

    /**

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

     */

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

    /**

     * 注册观察者对象

     * @param observer 观察者对象

     */

    public void attach(Observer observer) {

       observers.add(observer);

    }

    /**

     * 删除观察者对象

     * @param observer 观察者对象

     */

    public void detach(Observer observer) {

        observers.remove(observer);

    }

    /**

     * 通知所有注册的观察者对象

     */

    protected void notifyObservers() {

       for(Observer observer : observers){

           observer.update(this);

       }

    }

}

(2)接下来看看具体的目标对象,示例代码如下:

/**

 * 具体的目标对象,负责把有关状态存入到相应的观察者对象,

 * 并在自己状态发生改变时,通知各个观察者

 */

public class ConcreteSubject extends Subject {

    /**

     * 示意,目标对象的状态

     */

    private String subjectState;

    public String getSubjectState() {

       return subjectState;

    }

    public void setSubjectState(String subjectState) {

       this.subjectState = subjectState;

       //状态发生了改变,通知各个观察者

       this.notifyObservers();

    }

}

(3)再来看看观察者的接口定义,示例代码如下:

/**

 * 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象

 */

public interface Observer {

    /**

     * 更新的接口

     * @param subject 传入目标对象,好获取相应的目标对象的状态

     */

    public void update(Subject subject);

}

(4)接下来看看观察者的具体实现示意,示例代码如下:

/**

 * 具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致

 */

public class ConcreteObserver implements Observer {

    /**

     * 示意,观者者的状态

     */

    private String observerState;

   

    public void update(Subject subject) {

       // 具体的更新实现

       //这里可能需要更新观察者的状态,使其与目标的状态保持一致

       observerState = ((ConcreteSubject)subject)

.getSubjectState();

    }

}

12.2.4  使用观察者模式实现示例

要使用观察者模式来实现示例,那就按照前面讲述的实现思路,把报纸对象当作目标,然后订阅者当做观察者,就可以实现出来了。

使用观察者模式来实现示例的结构如图12.4所示:

 

图12.4  使用观察者模式来实现示例的结构示意图

还是来看看具体的代码实现。

(1)被观察的目标

       在前面描述的订阅报纸的例子里面,多个订阅者都是在观察同一个报社对象,这个报社对象就是被观察的目标。这个目标的接口应该有些什么方法呢?还是从实际入手去想,看看报社都有些什么功能。报社最基本有如下的功能:

  • 注册订阅者,也就是说很多个人来订报纸,报社肯定要有相应的记录才行
  • 出版报纸,这个是报社的主要工作
  • 发行报纸,也就是要把出版的报纸发送到订阅者手中
  • 退订报纸,当订阅者不想要继续订阅了,可以取消订阅

上面这些功能是报社最最基本的功能,当然,报社还有很多别的功能,为了简单起见,这里就不再去描述了。因此报社这个目标的接口也应该实现上述功能,把他们定义在目标接口里面,示例代码如下:

/**

 * 目标对象,作为被观察者

 */

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);

    }

    /**

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

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

     */

    protected void notifyObservers() {

       for(Observer reader : readers){

           reader.update(this);

       }

    }

}

       细心的朋友可能会发现,这个对象并没有定义出版报纸的功能,这是为了让这个对象更加通用,这个功能还是有的,放到具体报纸类里面去了,下面就来具体的看看具体的报纸类的实现。

为了演示简单,在这个实现类里面增添一个属性,用它来保存报纸的内容,然后增添一个方法来修改这个属性,修改这个属性就相当于出版了新的报纸,并且同时通知所有的订阅者。示例代码如下:

/**

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

 */

public class NewsPaper extends Subject{

    /**

     * 报纸的具体内容

     */

    private String content;

    /**

     * 获取报纸的具体内容

     * @return 报纸的具体内容

     */

    public String getContent() {

       return content;

    }

 

    /**

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

     * @param content 报纸的具体内容

     */

    public void setContent(String content) {

       this.content = content;

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

       notifyObservers();

    }

}

(2)观察者

目标定义好过后,接下来把观察者抽象出来,看看它应该具有什么功能。分析前面的描述,发现观察者只要去邮局注册了过后,就是等着接收报纸就好了,没有什么其它的功能。那么就把这个接收报纸的功能抽象成为更新的方法,从而定义出观察者接口来,示例代码如下:

/**

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

 */

public interface Observer {

    /**

     * 被通知的方法

     * @param subject 具体的目标对象,可以获取报纸的内容

     */

    public void update(Subject subject);

}

定义好了观察者的接口过后,该来想想如何实现了。具体的观察者需要实现:在收到被通知的内容后,自身如何进行相应处理的功能。为了演示的简单,收到报纸内容过后,简单的输出一下,表示收到了就行了。

定义一个简单的观察者实现,示例代码如下:

/**

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

 */

public class Reader implements Observer{

    /**

     * 读者的姓名

     */

    private String name;

 

    public void update(Subject subject) {

       //这是采用拉的方式

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

+((NewsPaper)subject).getContent());

    }

 


    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

}

(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.attach(reader1);

       subject.attach(reader2);

       subject.attach(reader3);

      

       //要出报纸啦

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

    }

}

测试一下看看,输出结果如下:

张三收到报纸了,阅读先。内容是===本期内容是观察者模式

李四收到报纸了,阅读先。内容是===本期内容是观察者模式

王五收到报纸了,阅读先。内容是===本期内容是观察者模式

       你还可以通过改变注册的观察者,或者是注册了又退订,来看看输出的结果。会发现没有注册或者退订的观察者是收不到报纸的。

       如同前面的示例,读者和报社是一种典型的一对多的关系,一个报社有多个读者,当报社的状态发生改变,也就是出版新报纸的时候,所有注册的读者都会得到通知,然后读者会拿到报纸,读者会去阅读报纸并进行后续的操作。

 


---------------------------------------------------------------------------

私塾在线学习网原创内容  跟着cc学设计系列 之 研磨设计模式

研磨设计讨论群【252780326】

原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/0/5280.html

---------------------------------------------------------------------------

相关笔记推荐
精品视频课程推荐

研磨设计模式——跟着cc学设计系列视频教程
本视频课程是北京Java私塾原创精品书籍《研磨设计模式》一书的配套学习视频,由《研磨设计模式》的第一作者CC录制 课程目标:全面、系统的掌握GoF设计模式的知识,达到可以在实际项目开发中运用的能力 技术要点:如何实现可配置、如何实现缓存以及缓存的管理、如何实现用缓存来控制多实例的创建、如何实现参数化工厂、 如何实现可扩展工厂、如何实现原型管理器、如何实现Java的静态代理和动态代理、如何实现多线程处理队列请求、 如何实现命令的参数化配置、可撤销的操作、宏命令、队列请求和日志请求、如何实现翻页迭代、如何检测环状结构、 如何实现通用的增删改查、如何模拟工作流来处理流程、如何实现简单又通用的XML读取、如何实现模拟AOP的功能......

freevoice(局域网对讲机)——Android4项目实战视频教程
实现通过Wifi来在局域网内相互传递信息和语音通话的功能;学习Service;Broadcast Receiver;SharedPreference;SQLite;语音录制、播放、传输等;网络开发:wifi处理、UDP编程、TCP编程;多种设计模式的应用 等等

软件系统功能设计实战训练视频教程
本课程是《软件系统功能设计实战训练》网络班的全套学习视频,通过6个具体的设计案例,综合应用几乎全部的GoF设计模式,以及多种设计理念和方法。 每个设计案例都各有侧重点,训练的难度会逐步加大,以切实帮助各位学员快速的掌握软件系统设计的方法,提升自己的软件系统设计能力。

Java高级视频教程-理论部分
Java程序设计基础、设计模式基础、框架的基本概念等知识。

《设计模式综合项目实战》——跟着cc学设计系列精品视频教程

浏览(4187)|评论(1)   交流分类:Java|笔记分类: 研磨设计模式

评论(1)
1楼 seekbug  2013-01-18 引用

已阅。先看文字再看视频效果会更好一点。(虽然我现在认为, 看了文字几乎都没有必要再看视频了)

请登录后评论 登录

关于我们 | 联系我们 | 用户协议 | 私塾在线服务协议 | 版权声明 | 隐私保护

版权所有 Copyright(C)2009-2012 私塾在线学习网