设计模式实战应用之二:观察者模式
2013-12-04 18:42
489 查看
观察者模式的定义
观察者模式是应用最普遍的设计模式之一。著名的 MVC 模式就是观察者模式的应用之一;高性能网络应用框架 MINA 中的事件处理器也是观察者模式的应用之一,一旦有 I/O 事件,所有注册的 IoHandler 对象会被通知到,我们可以通过对这个接口的自定义扩展以实现相应业务处理;流媒体服务 Wowza Streaming Engine 的插件模块扩展开发也正是利用到了观察者模式,用户通过自定义模块对比如 com.wowza.wms.stream.IMediaStreamActionNotify 等接口的实现,可以捕捉到自己所关心的流的一系列事件,进而就可以对特定直播/点播频道进行监控了。Gof 把观察者模式归类到对象行为型模式,《设计模式:可复用面向对象软件的基础》对观察者模式做出了明确的定义:“Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.” 翻译过来就是:“定义了一些对象之间的一对多依赖关系,这样子当一个对象的状态发生变化时,所有依赖于这个对象的对象都会得到通知,并被自动更新”。
why 观察者模式?
把系统设计成一些互相合作的类有一个常见的弊端:需要维护相关对象之间的一致性。为了维护一致性而使各类紧密耦合,大大降低了模块的可复用性,这是我们不愿意看到的。
观察者模式的使用场合
当一个抽象模型具有一方依赖于另一方的两方面时,将它们封装在独立的对象中可以让你对它们独立地改变和复用。
当对于一个对象的改变需要同时改变其他对象,而且你不知道有多少对象需要改变。
当一个对象需要通知其他对象,又不能假定其他对象是谁,换句话说,你不希望这些对象紧密地耦合在一起。
《多线程高效读取缓冲区数据》需求
本文示例摒弃了那些枯燥无味、与我们项目毫无关联的鸭子啊报社啊葡萄园之类的,同时也因为抄来抄去而被用烂了的无聊话题,采用的是 CSDN 网友项目实战中真实遇到而提出来的一个问题作为案例进行分析。让我们切实感受设计模式带来的好处,领略设计模式的真正的威力,而不只是用来玩理论、侃大山。
这个是网友 xgPaul 发帖提出的一个讨论。标题是《如何实现多线程高效的读取一块缓冲区中的数据???》帖子链接是:http://bbs.csdn.net/topics/390658695。
帖子正文描述如下:
一条线程不断的对一块内存缓冲区进行写数据,同时几十条线程(几百个对象)要从该缓冲区中读取数据。这一过程如何实现高效与数据同步。(使用锁同步效率太低)
《多线程高效读取缓冲区数据》分析
xgPaul 遇到的这个问题,比较类似于上海抢拍车牌号的场景:一条线程不断地对当前价格进行刷新,同时几十条线程(几百个对象)对当前价格进行读取监控。用观察者模式效率比较好,可以解决由于线程竞争、加锁而带来的效率问题。把读数据的线程归为观察者,主题是缓冲区数据。一旦数据有更新,主题向观察者推送更新数据,这样推数据的做法效率很高。缓冲区做成主题,每个观察者都有一份自己关心的主题数据的本地备份,如果主题没有推数据过来,本地备份就是最新数据。当然,这么干稍耗空间,但是却换得多线程环境中效率上的大幅度提升,这就是所谓的用空间换时间。
java.lang.ThreadLocal 就是这种原理的一个实现。
《多线程高效读取缓冲区数据》类设计
本文重在讲解模式,所以仅以一个 int 类型的数据 onlinePlayersNum 模拟缓冲区数据。两个发布板,一个公开发布板一个内部发布板作为观察者。
由 BufferData 对象向两个发布板推数据,后者不需要去前者查询数据,只需要查询自己的状态是否改变以决定是否更新发布。具体类图设计如下:
《多线程高效读取缓冲区数据》时序图
《多线程高效读取缓冲区数据》源码实现
《设计模式:可复用面向对象软件的基础》的观察者模式中的 Subject 角色,在本文中是为 Subject 接口,源码:
《设计模式:可复用面向对象软件的基础》观察者模式中的 Observer 角色,在本文中是为 Observer 接口,源码:
《设计模式:可复用面向对象软件的基础》观察者模式中的 ConcreteSubject 角色,在本文中是为 BufferData 类,源码:
《设计模式:可复用面向对象软件的基础》观察者模式中的 ConcreteObserver 角色之一,在本文中是为 PublicDisplay 类,源码:
《设计模式:可复用面向对象软件的基础》观察者模式中的 ConcreteObserver 角色之二,在本文中是为 InternalDisplay 类,源码:
观察者模式是应用最普遍的设计模式之一。著名的 MVC 模式就是观察者模式的应用之一;高性能网络应用框架 MINA 中的事件处理器也是观察者模式的应用之一,一旦有 I/O 事件,所有注册的 IoHandler 对象会被通知到,我们可以通过对这个接口的自定义扩展以实现相应业务处理;流媒体服务 Wowza Streaming Engine 的插件模块扩展开发也正是利用到了观察者模式,用户通过自定义模块对比如 com.wowza.wms.stream.IMediaStreamActionNotify 等接口的实现,可以捕捉到自己所关心的流的一系列事件,进而就可以对特定直播/点播频道进行监控了。Gof 把观察者模式归类到对象行为型模式,《设计模式:可复用面向对象软件的基础》对观察者模式做出了明确的定义:“Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.” 翻译过来就是:“定义了一些对象之间的一对多依赖关系,这样子当一个对象的状态发生变化时,所有依赖于这个对象的对象都会得到通知,并被自动更新”。
why 观察者模式?
把系统设计成一些互相合作的类有一个常见的弊端:需要维护相关对象之间的一致性。为了维护一致性而使各类紧密耦合,大大降低了模块的可复用性,这是我们不愿意看到的。
观察者模式的使用场合
当一个抽象模型具有一方依赖于另一方的两方面时,将它们封装在独立的对象中可以让你对它们独立地改变和复用。
当对于一个对象的改变需要同时改变其他对象,而且你不知道有多少对象需要改变。
当一个对象需要通知其他对象,又不能假定其他对象是谁,换句话说,你不希望这些对象紧密地耦合在一起。
《多线程高效读取缓冲区数据》需求
本文示例摒弃了那些枯燥无味、与我们项目毫无关联的鸭子啊报社啊葡萄园之类的,同时也因为抄来抄去而被用烂了的无聊话题,采用的是 CSDN 网友项目实战中真实遇到而提出来的一个问题作为案例进行分析。让我们切实感受设计模式带来的好处,领略设计模式的真正的威力,而不只是用来玩理论、侃大山。
这个是网友 xgPaul 发帖提出的一个讨论。标题是《如何实现多线程高效的读取一块缓冲区中的数据???》帖子链接是:http://bbs.csdn.net/topics/390658695。
帖子正文描述如下:
一条线程不断的对一块内存缓冲区进行写数据,同时几十条线程(几百个对象)要从该缓冲区中读取数据。这一过程如何实现高效与数据同步。(使用锁同步效率太低)
《多线程高效读取缓冲区数据》分析
xgPaul 遇到的这个问题,比较类似于上海抢拍车牌号的场景:一条线程不断地对当前价格进行刷新,同时几十条线程(几百个对象)对当前价格进行读取监控。用观察者模式效率比较好,可以解决由于线程竞争、加锁而带来的效率问题。把读数据的线程归为观察者,主题是缓冲区数据。一旦数据有更新,主题向观察者推送更新数据,这样推数据的做法效率很高。缓冲区做成主题,每个观察者都有一份自己关心的主题数据的本地备份,如果主题没有推数据过来,本地备份就是最新数据。当然,这么干稍耗空间,但是却换得多线程环境中效率上的大幅度提升,这就是所谓的用空间换时间。
java.lang.ThreadLocal 就是这种原理的一个实现。
《多线程高效读取缓冲区数据》类设计
本文重在讲解模式,所以仅以一个 int 类型的数据 onlinePlayersNum 模拟缓冲区数据。两个发布板,一个公开发布板一个内部发布板作为观察者。
由 BufferData 对象向两个发布板推数据,后者不需要去前者查询数据,只需要查询自己的状态是否改变以决定是否更新发布。具体类图设计如下:
《多线程高效读取缓冲区数据》时序图
《多线程高效读取缓冲区数据》源码实现
《设计模式:可复用面向对象软件的基础》的观察者模式中的 Subject 角色,在本文中是为 Subject 接口,源码:
package com.defonds.buffer; public interface Subject { public void registerObserver(Observer observer); public void removeObserver(Observer observer); public void notifyObservers(); }
《设计模式:可复用面向对象软件的基础》观察者模式中的 Observer 角色,在本文中是为 Observer 接口,源码:
package com.defonds.buffer; public interface Observer { public void update(int onlineNum); }
《设计模式:可复用面向对象软件的基础》观察者模式中的 ConcreteSubject 角色,在本文中是为 BufferData 类,源码:
package com.defonds.buffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class BufferData implements Subject { private int onlinePlayersNum = 0; // store online players num private List<Observer> observers = new ArrayList<Observer>(); // refer to all observers @Override public void registerObserver(Observer observer) { this.observers.add(observer); // add one observer } @Override public void removeObserver(Observer observer) { int i = this.observers.indexOf(observer); if (i >= 0) { this.observers.remove(i); } } @Override public void notifyObservers() { Iterator<Observer> iterator = this.observers.iterator(); while (iterator.hasNext()) { iterator.next().update(this.onlinePlayersNum); } } public void setOnlinePlayersNum(int onlinePlayersNum) { this.onlinePlayersNum = onlinePlayersNum; this.notifyObservers(); } }
《设计模式:可复用面向对象软件的基础》观察者模式中的 ConcreteObserver 角色之一,在本文中是为 PublicDisplay 类,源码:
package com.defonds.buffer; public class PublicDisplay extends Thread implements Observer, Display { private int onlineNum; private boolean changed = false; @Override public void update(int onlineNum) { this.onlineNum = onlineNum; this.changed = true; } @Override public void display() { System.out.println("The current number of players online is : " + this.onlineNum); } @Override public void run() { while (this.changed) { this.display(); this.changed = false; } } }
《设计模式:可复用面向对象软件的基础》观察者模式中的 ConcreteObserver 角色之二,在本文中是为 InternalDisplay 类,源码:
package com.defonds.buffer; public class InternalDisplay extends Thread implements Observer, Display { private int oldNum = 0; private int onlineNum = 0; private boolean changed = false; @Override public void update(int onlineNum) { this.oldNum = this.onlineNum; this.onlineNum = onlineNum; this.changed = true; } @Override public void display() { System.out.println("The current number of players online is : " + this.onlineNum); if(this.onlineNum > this.oldNum) { System.out.println("The number of online upward"); } else { System.out.println("The number of online decline"); } } @Override public void run() { while (this.changed) { this.display(); this.changed = false; } } }
相关文章推荐
- 资源列表
- PHP基于SOAP实现webservice
- taskTracker和jobTracker 启动失败
- 图论连通性历程
- linux rsync详解
- How to find crash logs for iPhone, iPod Touch or iPad applications
- 为什么使用 SLF4J 而不是 Log4J 来做 Java 日志
- HDU 4612(缩点+求桥+求树的直径)
- 用C语言实现的简单Web服务器
- readprocessmemory error 299
- nodeschool.io 4
- 微策略面试题:在旋转后的数组中查找元素(二分查找)
- C# 动态调用WebService
- 期末成绩计算方法
- acm - cry for no one
- 移除LVM中的磁盘,给VG增加空间,减少空间
- Sicily1781(广搜)
- 一个是表ofroster,一个是服务,
- Uva 11292 Dragon of Loowater - 水题
- 求二维数组中的最大值