1. Brief                            

一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式。若有纰漏请大家指正。

2. Use Case                          

首先我们来面对一个老到跌渣的故事,并以从未听说过Observer Pattern为前提。

假设要设计一个新闻订阅系统,新闻分为商业、体育和八卦3种,而查收终端有PC、移动终端等,后续还不断增加新闻种类和查收终端。

需求如上,下面我们根据OOD的方式来构建概念模型。

新闻 <- 分类新闻

终端 <- 分类终端

然后构造实体模型

// 新闻相关实体模型
class NewsType{
constructor(){}
}
class BusinessNewsType extends NewsType{}
class SportNewsType extends NewsType{}
class EntertaintmentNewsType extends NewsType{} // 终端相关实体模型
class Term{
getNews(news){}
}
class PCTerm extend Term{}
class MobileTerm extend Term{}

接着我们关联已有经验——现实生活中的送报服务,发现用户A、用户B均订阅了A报的X早报,那么每天早上报纸刚印刷出来就会马上送到用户A和B那了。突然多了家订报有米送的Y早报,用户A和用户C跑去订阅,用户B直接就退了X早报,这时X早报为留住老用户就推送“老客户免费赠送半年X晚报”,于是用户A取消了退订的想法。

很明显 新闻订阅系统 就是线下业务直挪到线上的做法,通过分析线下业务流程我们可以找到设计方案。线下业务流程如下:

订阅:用户到报社订阅

退订:用户到报社退订

分发报纸:报社向所有订阅者分发报纸

按这思路构建 新闻订阅系统 的原型:

class NewsType{
constructor(){
this.subs = []
}
/* 订阅
* @param {Term} term - 终端
* @returns {Boolean}
*/
sub(term){
// 排除
for(let _sub of this.subs)
if(_sub === term) return false return Boolean(this.subs.push(term))
}
/* 退订
* @param {Term} term - 终端
* @return {Boolean}
*/
unsub(term){
for(let i = , sub; sub = subs[i]; ++i)
if(sub === term) return Boolean(this.subs.splice(i, ))
}
/*
* 分发新闻
*/
notify(news){
for(let sub of this.subs) sub.getNews(news)
}
}
class BusinessNewsType extends NewsType{
pubNews(title, content){
var news = {title: title, content: content}
super.notify(news)
}
}
// definition of SportNewsType....... class PCTerm extends Term{
getNews(news){
alert(news.title + ';' + news.content)
}
}
class MobileTerm extends Term{
getNews(news){
console.log(news.title + ';' + news.content)
}
} // 主程序则作为 新闻中心 与 用户交互的场所
var businessNewsType = new BusinessNewsType()
var sportNewsType = new SportNewsType()
var pcTerm = new PCTerm()
var mobileTerm = new MobileTerm()
businessNewsType.sub(pcTerm)
businessNewsType.sub(mobileTerm)
sportNewsType.sub(mobileTerm)

上述原型基本勾勒出新闻订阅系统中对象及其关联的方式,我们就可以在这之上再细化和优化了。而从上述是原型我们不难发现 新闻 与 终端 均可独立开发,然后在主程序中做关联即可。新闻类型 和 终端类型的增删并不会对其他已有的新闻类型和终端类型有影响,除了在主程序中增删关联外。

现在我们作个简单的分析总结:

1. 不稳定因素(新闻类型 和 终端类型)解耦 -> 最小化不稳定因素所影响的范围(范围越小,后期改动越少);

2. 关联规则接口/契约化 -> 固化关联规则 和 关联发生的形式 便于后期维护。

这些是我面对未知问题的分析、解构方法,希望和大家一起探讨更美好的方法。

3. What Is Observer Pattern?                

Observer Pattern(观察者模式),狭义上是指Observer/Subscriber关注Observable/Subject的状态,并根据Observable/Subject的状态作出响应。广义上是指Observer/Subscriber关注Observable/Subject的状态或行为或两者兼备,并作出响应。

Roles

Observable/Subject(被观察者):定义被观察者的公共状态和行为

ConcreteObservable(具体的被观察者):定义具体的被观察者的状态和行为

Observer(观察者):定义观察者的公共状态和行为

ConcreteObserver(具体的观察者):定义具体的观察者的状态和行为

Two Methods: Push & Pull

上面第2节中的实现是由Observable/Subject来维护Observer组,那是不是只能这样呢?答案是否定的。它只是Push方式的实现,我们还可以采用Pull方式呢!

Push Model:推方式,也就是由Observable/Subject主动发起与Observer/Subscriber通信,并将自身的所有信息推给Observer/Subscriber,即使大部分信息最后都没用上。

  pros: 1. 观察者实时响应被观察者的状态变化和行为状况;

cons: 1. 观察者被硬塞一些被观察者的无效信息;2. 被观察者状态变化频密,导致观察者忙于响应,消耗资源。

Pull Model:拉方式,也就是由主动Observer/Subscriber发起与Observable/Subject通信,并根据自身需要从Observable/Subject上拉去有效信息。一般通过定时器或特定事件触发执行。

pros: 1. 观察者可按需从被观察者处提取有效信息;2. 自主控制通信节奏,以免被状态频密变化的被观察者牵着鼻子走;

cons: 1. 获取被观察者状态变化上存在滞后甚至丢失的情况。

下面是Pull Model的实现方式

// Pull Model implementation
class FooNewsType{
constructor(){
this.news = []
} addNews(title, content){
this.news.push({title: title, content: content, timestamp:(+new Date())})
}
} class PCTerm{
constructor(){
this.subjects = []
this.newsTiles = []
this.lastPullDate =
} subTo(newsType){
this.subjects.push(newType)
}
unsubFrom(newsType){
for(let i = , n; n = this.subjects[i]; ++i)
if(n === newsType) return this.subjects.splice(i, )
}
// 拉数据
pull(){
for(let sub of this.subjects)
for(let news of sub.news)
if(news.timestamp > this.lastPullDate)
this.newsTiles .push(news.title)
}
} // 主程序
var businessNewsType = new BusinessNewsType()
var pcTerm = new PCTerm()
pcTerm.subTo(businessNewsType)
businessNewsType .addNews('Say Hi', 'Hello World!')
// 其他代码........
pcTerm.pull()

  Improvement of Push Model 

针对Push Model所带来的问题1,我们可以通过增强sub函数来解决

// definition
sub(term, aspect){
this.subs.push({term: term, aspect: aspect})
}
notify(news){
for(let sub of this.subs)
sub.term.getNews(sub.aspect && sub.aspect(news) || news)
} // usage
new BusinessNewsType()
.sub(new PCTerm()
, (news)=>{ return news.title })

针对问题2,我们可以通过 定时推送通知 + 溢出通知 的方式解决,不过具体还是看业务需求咯

constructor(interval = , ceiling = ){
this.ceiling = ceiling
this.timer = setInterval(()=>{
if (!this.pools.length || !this.subs.length) return for(let sub of this.subs)
for(let n of news)
sub.term.getNews(sub.aspect && sub.aspect(n) || n)
}, interval)
}
notify(news){
this.pools.push(news)
if (this.pools.length < this.ceiling) return var news = this.pools.splice(, this.pools.length)
for(let sub of this.subs)
for(let n of news)
sub.term.getNews(sub.aspect && sub.aspect(n) || n)
}

  Specific Implementation Problems —— Making sure Subject state is self-consistent before notification

就是确保Subject状态变化完成后,再通知Subscriber。反例如下:

notify(news){
for(let sub of this.subs)
sub.term.getNews(sub.aspect && sub.aspect(news) || news)
// 发生在通知观察者之后
news.title = 'changed'
}

相当于为每次Subject状态的整体变化打个版本号,然后将属于该版本的Subject状态发送给Subscriber,之后的状态变化就属于下一个版本了。

4. Diff Between Observer Pattern and Pub/Sub Pattern

两者区别主要体现在以下2点

  1. 耦合度

Observer Pattern: Subscriber 和 Subject 两者感知对方的存在,但不受对方的具体实现 和 数目 所限制 => 弱依赖。关联规则内置在Subscriber 或 Subject中。

Pub/Sub Pattern: Publisher 和 Subscriber 两者相互间毫无存在感,通过Message Broker关联两种角色,并且将关联规则藏进Message Broker中。

    2. 影响范围

Observer Pattern作为Design Pattern存在,而Pub/Sub Pattern则作为Architecture Pattern存在,明显Observer Pattern的影响范围较小。也就是说在采用Pub/Sub Pattern时,需要更谨慎。

5. We Used Observer Pattern Already           

其实我们现在用到很多框架、类库均采用了Observer Pattern,如MVC和Event Mechanism等。

MVC中M(odel)作为观察者,而V(iew)作为被观察者;

而Event Mechanism则是更为典型的Observer Pattern,C#在语法层面(event关键字),而Java通过内置类库对其提供支持。

6. Conclusion                        

洋洋洒洒写了这么多,若有纰漏请大家指正,谢谢!

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4627487.html ^_^肥子John

7. Thanks                          

http://wiki.jikexueyuan.com/project/javascript-design-patterns/observer-pattern.html

http://www.joezimjs.com/javascript/javascript-design-patterns-observer/

http://www.oodesign.com/observer-pattern.html

Design Pattern: Observer Pattern的更多相关文章

  1. [Design Pattern] Observer Pattern 简单案例

    Observer Pattern,即观察者模式,当存在一对多关系,例如一个对象一有变动,就要自动通知被依赖的全部对象得场景,属于行为类的设计模式. 下面是一个观察者模式的简单案例. Observer ...

  2. 设计模式复习小结一(Strategy Pattern/Observer Pattern/Decorator Patter/Factory Pattern)

    目录: 前言 1. Stratrgy Pattern 2. Observer Pattern 3. Decorator Pattern 4. Factory Pattern 4.1 FactoryPa ...

  3. Learning JavaScript Design Patterns The Observer Pattern

    The Observer Pattern The Observer is a design pattern where an object (known as a subject) maintains ...

  4. [Design Patterns] 03. Behavioral Patterns - Observer Pattern

    前言 参考资源 Ref: 史上最全设计模式导学目录(完整版) 观察者模式-Observer Pattern[学习难度:★★★☆☆,使用频率:★★★★★] 对象间的联动——观察者模式(一):多人联机对战 ...

  5. 深入浅出设计模式——观察者模式(Observer Pattern)

    模式动机 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应.在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而 ...

  6. Observer Pattern

    Motivation We can not talk about Object Oriented Programming without considering the state of the ob ...

  7. 设计模式(二)The Observer Pattern 观察者模式

    问题引入 生成一个公告板显示当时的天气状况,当天气状况发生改变的时候公告板能够实时的更新. 模式定义 定义对象之间的一对多的依赖.当一个对象改变状态时,它的全部依赖者都会自己主动收到通知并自己主动更新 ...

  8. 设计模式系列之观察者模式(Observer Pattern)

    意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作 ...

  9. 24种设计模式--观察者模式【Observer Pattern】

     <孙子兵法>有云: “知彼知己,百战不殆:不知彼而知己,一胜一负:不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好 ...

随机推荐

  1. 【腾讯Bugly干货分享】微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ff5932cde42f1f03de29b1 本文来源: 微信客户端开发团队 ...

  2. STC12C5A60S2笔记3(I/O 端口)

    1. 基本特性 STC 单片机IO口可由软件配置成四种工作模式: 1)准双向口:标准51单片机模式,可用作输入和输出 2) 推挽输入输出:需要更大的驱动电流,当高电平时,拉电流20mA.当低电平时,灌 ...

  3. 在tomcat下部署工程

    xx系统第一期工程完成,今天老大要我去部署系统,从来就没有在tomcat下部署过,一直都是在myeclipse下部署.启动.运行即可,所以这次遇到了几个问题,记录下来. tomcat启动 在安装tom ...

  4. SOA服务设计与实现的几个语言无关的原则速记

    一.SOA定义 SOA即面向服务架构(Service-Oriented Architecture).在SOA中,一切皆服务.一个服务是通过消息交换来调用的程序,一个信息系统是共同完成一个特定任务的一组 ...

  5. DNS 正向查找与反向查找

    原创地址:http://www.cnblogs.com/jfzhu/p/3996323.html 转载请注明出处 所谓正向查找,就是说在这个区域里的记录可以依据名称来查找对应的IP地址.反向查找就是在 ...

  6. Android开发学习之路-使用Handler和Message更新UI

    在Android中,在非主线程中更新UI控件是不安全的,app在运行时会直接Crash,所以当我们需要在非主线程中更新UI控件,那么就需要用到Handler和Message来实现 Demo中,使用到一 ...

  7. Java线程:线程栈模型与线程的变量

    Java线程:线程栈模型与线程的变量   要理解线程调度的原理,以及线程执行过程,必须理解线程栈模型. 线程栈是指某时刻时内存中线程调度的栈信息,当前调用的方法总是位于栈顶.线程栈的内容是随着程序的运 ...

  8. nginx 配置管理 - 简单也复杂

    由于涉及到h5与后端交互,跨域问题,所以公司的开放测试服务器让我们自己搞nginx.顺便提升一下nginx的实践. nginx的安装,没什么难度了,百度一堆,如果源码安装就一步步来吧.(最简单的方式: ...

  9. fir.im Weekly - 进击的 Swift

    最近 Swift 开源了,众开发者们欢呼雀跃.感谢开源,这是最好的时代.本期 fir.im Weekly 准备了一些关于 Swift 的"新鲜"干货分享,也包括一些优秀的 GitH ...

  10. 修复发生“由于数据移动,未能继续以 NOLOCK 方式扫描”错误的数据库

    最近在系统运行中发现了一个错误,错误信息如下: 错误信息:查询A201412C20568单证信息错误 ---> System.Data.OleDb.OleDbException: 由于数据移动, ...