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. HTML5优秀图表控件

    不管是哪个领域的开发,都有机会用到图表来做统计分析,以更直观的表现形式来代替传统的文字.在以前,图表控件主要有使用程序代码生成的静态图片,或者是使用flash实现的图表控件. 在HTML5非常流行的当 ...

  2. 从一个简单例子来理解js引用类型指针的工作方式

    <script> var a = {n:1}; var b = a; a.x = a = {n:2}; console.log(a.x);// --> undefined conso ...

  3. ENode 1.0 - Command Service设计思路

    开源地址:https://github.com/tangxuehua/enode 上一篇文章,介绍了enode框架的物理部署思路.本文我们再简单分析一下Command Service的API设计: C ...

  4. Android下集成Paypal支付

    近期项目需要研究paypal支付,官网上的指导写的过于复杂,可能是老外的思维和中国人不一样吧.难得是发现下面这篇文章: http://www.androidhive.info/2015/02/andr ...

  5. 记录maven java.lang.String cannot be cast to XX error

    在项目开发中自定义了一个maven plugin,在本地能够很好的工作,但是在ci server上却无法正常工作报错为: --------------------------------------- ...

  6. Silverlight中文本框添加回车事件后,换行无法清除的解决方法

    在开发Silverlight的项目中,为了更好的用户体验,我们常要给一些控件添加一些快捷键.然而,在Silverlight中当用户回车提交后,光标停留在文本框的第二行怎么也清除不掉,经过一段时间研究, ...

  7. 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——理论篇

    工作两年多了,我会经常尝试给公司小伙伴儿们解决一些问题,几个月下来我发现初入公司的小朋友最爱问的问题就三个 1. 我想前台调用后台的XXX方法怎么弄啊? 2. 我想后台调用前台的XXX JavaScr ...

  8. SVNServer迁移

    本文主要记录SVNServer从windows系列迁移到Centos的过程,以下几篇文章已经描述的很好了,本文就不再赘述. 注意问题: 配置权限时空格问题:包括前面不能有空格,=两边有空格等. 参考链 ...

  9. MVVM架构~knockoutjs系列之为Ajax传递Ko数组对象

    返回目录 一些要说的 这是一个很有意思的题目,在KO里,有对象和数组对象两种,但这两种对象对外表现都是一个function,如果希望得到他的值,需要进行函数式调用,如ko_a(),它的结果为一个具体值 ...

  10. JavaScript模板引擎实例应用

    在之前的一篇名为<移动端基于HTML模板和JSON数据的JavaScript交互>的文章中,我向大家说明了为什么要使用JavaScript模板以及如何使用,文末还提到了laytpl.art ...