一.什么是观察者模式?

把现实世界中的报纸与订阅者的关系抽象出来就是观察者模式,一种报纸对应多个订阅者,订阅者可以随时解除订阅,未订阅的读者也可以随时开始订阅。一旦有新报纸发布,所有的订阅者都会收到新内容。

在观察者模式中,报纸叫做主题Subject,订阅者叫做观察者Observer,一个Subject可以被多个Observer关注,Observer可以随时解除关注,新的Observer也可以随时关注Subject。Subject内容发生改变时,会通知所有的Observer。

二.举个例子

很多网络游戏中都有答题活动,所有参与答题活动的玩家都会同时收到题目信息(延迟忽略不计),未参与活动的玩家可以中途加入,正在答题的玩家也可以随时退出。

在此例中,游戏服务器就是“一”,玩家是“多”,题目信息就是在它们之间传递的消息。

怎样才能设计出满足以上要求的类?不妨试试观察者模式。

首先,定义Subject抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package ObserverPattern;
 
import java.util.ArrayList;
 
/**
 * @author ayqy
 * 定义Subject抽象类
 *
 */
public abstract class Subject {
    ArrayList<Observer> observers = new ArrayList<Observer>();//观察者列表
     
    /**
     * 注册主题
     * @param o 申请注册该主题的Observer
     */
    public void registSubject(Observer o)
    {
        observers.add(o);
    }
     
    /**
     * 删除主题
     * @param o
     */
    public void reomveSubject(Observer o)
    {
        int index = observers.indexOf(o);
        observers.remove(index);
    }
     
    /**
     * 通知所有观察者
     * @param arg 该Subject想要传递给Observers的数据
     */
    public void notifyObservers(Object arg)
    {
        for(Observer o : observers)
            o.update(this, arg);
    }
}

注意,这里用了抽象类而没有用接口,为什么?

因为Subject的行为是fixed的,并不需要由子类来扩展。

-------

类似的,我们定义Observer抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
package ObserverPattern;
 
/**
 * @author ayqy
 * 定义Observer抽象类
 *
 */
public abstract class Observer {
    Subject subject = null;//定义该Observer所关注的Subject
     
    public abstract void update(Subject subject, Object arg);//定义Observer的更新接口
}

-------

下面开始实现我们的自定义Subject——GameServer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package ObserverPattern;
 
/**
 * @author ayqy
 * 定义GameServer类,继承自Subject基类,负责发布题目
 *
 */
public class GameServer extends Subject{
    Question ques;//定义题目
 
    public Question getQues() {
        return ques;
    }
 
    public void setQues(Question q) {
        this.ques = q;
         
        super.notifyObservers(ques);//调用父类方法通知所有Observer
    }
}

P.S.GameServer类的成员变量Question是对题目信息的简单封装,Question类包含题号no与题目内容content两部分定义,以及一个toString方法,返回题目描述信息

-------

再实现我们的自定义Observer——Player:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package ObserverPattern;
 
/**
 * @author ayqy
 * 定义PlayerA,继承自Observer基类,负责接收新题目
 *
 */
public class PlayerA extends Observer{
     
    public PlayerA(Subject sub)
    {
        subject = sub;
    }
 
    @Override
    public void update(Subject subject, Object arg) {
        Question q = (Question)arg;
        System.out.println("PlayerA received " + q.toString());
    }
     
}

P.S.为了使类层次更加清晰,此处并没有定义Player基类

很容易复制粘贴得到PlayerB与PlayerC,不再赘述

至此,我们的模拟答题活动准备工作已经结束了,下面我们需要定义一个测试类来展示观察者模式的魅力。

三.效果示例

定义如下Test类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package ObserverPattern;
 
/**
 * @author ayqy
 * 实现一个测试类,模拟网络游戏答题活动(游戏服务器按时更新题目信息并通知所有参与答题的玩家)
 *
 */
public class Test {
    public static void main(String[] args)
    {
        System.out.println("答题活动即将开始。。");
        //创建服务器
        GameServer gs = new GameServer();
        //创建玩家ABC
        Observer playerA = new PlayerA(gs);
        Observer playerB = new PlayerB(gs);
        Observer playerC = new PlayerC(gs);
        //为AB注册Subject,C对答题不感兴趣,拒绝注册
        gs.registSubject(playerA);
        gs.registSubject(playerB);
        System.out.println("玩家AB成功参与答题活动。。");
        System.out.println("答题活动正式开始。。");
        gs.setQues(new Question(1, "第一题"));
        gs.setQues(new Question(2, "第二题"));
        System.out.println("玩家A不想玩了,退出答题活动。。");
        gs.reomveSubject(playerA);
        gs.setQues(new Question(3, "第三题"));
        System.out.println("玩家C想中途加入活动");
        gs.registSubject(playerC);
        gs.setQues(new Question(4, "第四题"));
        System.out.println("答题活动结束。。");
    }
}

结果示例:

四.总结

从上面的例子可以看出观察者模式的特点:

1.利用观察者模式可以轻易地建立对象之间“一对多”的依赖关系

2.利用观察者模式的机制可以很容易的实现这种依赖关系的动态维护

<原创>黯羽轻扬 欢迎转载 不必注明原文出处</原创>
<声明>作者水平有限 错误在所难免 欢迎指正</声明>
<邮箱>835412398@qq.com 交流方可进步</邮箱>

设计模式之观察者模式(Observer Pattern)的更多相关文章

  1. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  2. 二十四种设计模式:观察者模式(Observer Pattern)

    观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...

  3. [设计模式] 19 观察者模式 Observer Pattern

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对观察者模式是这样说的:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.当一个 ...

  4. c#设计模式之观察者模式(Observer Pattern)

    场景出发 一个月高风黑的晚上,突然传来了尖锐的猫叫,宁静被彻底打破,狗开始吠了,大人醒了,婴儿哭了,小偷跑了 这个过程,如果用面向对象语言来描述,简单莫过于下: public class Cat { ...

  5. 设计模式之观察者模式(Observer pattern)

    最近参加了一次面试,其中笔试题有一道编程题,在更换掉试题的描述场景后,大意如下: 上课铃声响起,学生A/B/C/D进入教室:下课铃声响起,学生A/B/C/D离开教室. 要求使用设计模式的思想完成铃与学 ...

  6. 设计模式九: 观察者模式(Observer Pattern)

    简介 观察者属于行为型模式的一种, 又叫发布-订阅模式. 如果一个对象的状态发生改变,依赖他的对象都将发生变化, 那么这种情况就适合使用观察者模式. 它包含两个术语,主题(Subject),观察者(O ...

  7. 【设计模式】观察者模式 Observer Pattern

    定义:观察者模式定义了对象之间的一对多依赖.当“主题”(Object)状态改变事,所有依赖它的“观察者”(Observer)都会受到通知并自动更新.主题支持观察者订阅和退订. 观察者模式提供了一种对象 ...

  8. 【UE4 设计模式】观察者模式 Observer Pattern

    概述 描述 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新.观察者模式又叫做 发布-订阅(Publish/Subscribe)模式 模型-视图(M ...

  9. 设计模式-观察者模式(Observer Pattern)

    观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...

  10. 设计模式 - 观察者模式(Observer Pattern) 详细说明

    观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

随机推荐

  1. mysql 参数:[read_buffer_size] [sort_buffer_size] [read_rnd_buffer_size] [tmp_table_size]---图解

    http://imysql.cn/2008_09_27_deep_into_mysql_sort_buffer http://my.oschina.net/realfighter/blog/36442 ...

  2. dump_stack的简单使用 +CALL TREE

    http://blog.chinaunix.net/uid-26403844-id-3361770.html http://blog.csdn.net/zifeng274059226/article/ ...

  3. 用 Qt 中的 QDomDocument类 处理 XML 文件(上)

      我们可以看到,如果所要读取的XML文件不是很大,采用DOM读取方法还是很便捷的,由于我用的也是DOM树读取的方法,所以,本文所介绍的也主要是基于DOM的方法读取. 根据常用的操作,我简单的把对XM ...

  4. html+css3实现网页时钟

    在网页上实现时钟功能,效果如右图所示: 运用到的关键技术有:css3中的旋转.旋转基点设置 旋转:transform:rotate(*deg) 旋转基点:transform-origin: x轴偏移 ...

  5. excel中VBA对多个文件的操作

    添加引用 "Scripting.FileSystemObject" (Microsoft Scripting Runtime) '用于操作文件.目录 Sub 数据整理部分() ' ...

  6. c# 双问号运算

    model.id??0 ??运算:如果运算符左边的值为NULL侧返回右边的值,否则返回左边的值

  7. 浅析CSS中的haslayout

    作为一名web开发人员,最大的希望不是自己的水平有多高,而是希望浏览器厂家能够统一标准,相信任何一个只要是接触过web程序开发的人员都有那样的感受,就是浏览器之间的兼容性问题总是让我们的工作平添诸多的 ...

  8. 从 man 指令起步(info简介)

    前言 小生认为一切指令的学习首先要从帮助入手,深入了解它的功能,即使是在实际项目中我们都离不开它的帮助.因为我们不一定能够记住全部指令的全部的相关功能,因此,查看指令的帮助是我们的不二选择. 正文 下 ...

  9. free 堡垒机

    环境: centos6.5 mini安装 iptables selinux已经关闭 jumpserver: 192.168.1.209 testserver: 192.168.1.210 一. 部署l ...

  10. seaJs初体验

    目录结构 模块定义define define(function(require,exports,module){ //exports可以把方法或属性暴露给外部 exports.name = 'hell ...