设计模式实战应用之二:观察者模式
观察者模式的定义
观察者模式是应用最普遍的设计模式之一。著名的 MVC 模式就是观察者模式的应用之一。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; } } }
相关文章
- ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现
- Ioc 器管理的应用程序设计,前奏:容器属于哪里? 控制容器的反转和依赖注入模式
- 详解 Redis 应用场景及应用实例
- 【快应用】account.authorize授权码模式登录报错1102
- 【开发者必看】移动应用趋势洞察白皮书-游戏篇
- Android 仿PhotoShop调色板应用(三) 主体界面绘制
- macOS SwiftUI 应用架构之全屏模式 03 保留对基本功能的访问权限和配置辅助窗口以在全屏模式下使用
- macOS SwiftUI 应用架构之全屏模式 01 仅在合理的情况下启用全屏窗口
- Excel 数据透视表教程大全之 02 添加字段、设置数据格式应用货币模式、按值进行排序(教程含样本数据)
- 大数据架构和模式(五)——对大数据问题应用解决方案模式并选择实现它的产品
- Selenium2学习-036-WebUI自动化实战实例-034-JavaScript 在 Selenium 自动化中的应用实例之六(获取 JS 执行结果返回值)
- 《Storm企业级应用:实战、运维和调优》——1.5 Storm的功能
- ThinkPHP6项目基操(8.多应用模式)
- 基于大数据应用探索“互联网+卷烟市场监管”新模式
- 单例模式的常见应用场景
- 智慧城市项目在PPP模式中的应用
- checkbox 与JS的应用
- 在JSP中应用JavaBean
- Matlab中的DSP应用中的bilinear函数
- 解决 centos 7 部署 tomcat 后外部不能访问应用(端口、防火墙)
- 从中煤陕西公司看政企移动信息化应用
- SD-WAN技术在海外游戏行业应用前景?
- 领域驱动系列二策略模式的应用
- Java-OO接口-DAO模式代码阅读及应用
- 企业微信自建应用开发