Design Pattern: Observer Pattern
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的更多相关文章
- [Design Pattern] Observer Pattern 简单案例
Observer Pattern,即观察者模式,当存在一对多关系,例如一个对象一有变动,就要自动通知被依赖的全部对象得场景,属于行为类的设计模式. 下面是一个观察者模式的简单案例. Observer ...
- 设计模式复习小结一(Strategy Pattern/Observer Pattern/Decorator Patter/Factory Pattern)
目录: 前言 1. Stratrgy Pattern 2. Observer Pattern 3. Decorator Pattern 4. Factory Pattern 4.1 FactoryPa ...
- Learning JavaScript Design Patterns The Observer Pattern
The Observer Pattern The Observer is a design pattern where an object (known as a subject) maintains ...
- [Design Patterns] 03. Behavioral Patterns - Observer Pattern
前言 参考资源 Ref: 史上最全设计模式导学目录(完整版) 观察者模式-Observer Pattern[学习难度:★★★☆☆,使用频率:★★★★★] 对象间的联动——观察者模式(一):多人联机对战 ...
- 深入浅出设计模式——观察者模式(Observer Pattern)
模式动机 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应.在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而 ...
- Observer Pattern
Motivation We can not talk about Object Oriented Programming without considering the state of the ob ...
- 设计模式(二)The Observer Pattern 观察者模式
问题引入 生成一个公告板显示当时的天气状况,当天气状况发生改变的时候公告板能够实时的更新. 模式定义 定义对象之间的一对多的依赖.当一个对象改变状态时,它的全部依赖者都会自己主动收到通知并自己主动更新 ...
- 设计模式系列之观察者模式(Observer Pattern)
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作 ...
- 24种设计模式--观察者模式【Observer Pattern】
<孙子兵法>有云: “知彼知己,百战不殆:不知彼而知己,一胜一负:不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好 ...
随机推荐
- HTML5优秀图表控件
不管是哪个领域的开发,都有机会用到图表来做统计分析,以更直观的表现形式来代替传统的文字.在以前,图表控件主要有使用程序代码生成的静态图片,或者是使用flash实现的图表控件. 在HTML5非常流行的当 ...
- 从一个简单例子来理解js引用类型指针的工作方式
<script> var a = {n:1}; var b = a; a.x = a = {n:2}; console.log(a.x);// --> undefined conso ...
- ENode 1.0 - Command Service设计思路
开源地址:https://github.com/tangxuehua/enode 上一篇文章,介绍了enode框架的物理部署思路.本文我们再简单分析一下Command Service的API设计: C ...
- Android下集成Paypal支付
近期项目需要研究paypal支付,官网上的指导写的过于复杂,可能是老外的思维和中国人不一样吧.难得是发现下面这篇文章: http://www.androidhive.info/2015/02/andr ...
- 记录maven java.lang.String cannot be cast to XX error
在项目开发中自定义了一个maven plugin,在本地能够很好的工作,但是在ci server上却无法正常工作报错为: --------------------------------------- ...
- Silverlight中文本框添加回车事件后,换行无法清除的解决方法
在开发Silverlight的项目中,为了更好的用户体验,我们常要给一些控件添加一些快捷键.然而,在Silverlight中当用户回车提交后,光标停留在文本框的第二行怎么也清除不掉,经过一段时间研究, ...
- 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——理论篇
工作两年多了,我会经常尝试给公司小伙伴儿们解决一些问题,几个月下来我发现初入公司的小朋友最爱问的问题就三个 1. 我想前台调用后台的XXX方法怎么弄啊? 2. 我想后台调用前台的XXX JavaScr ...
- SVNServer迁移
本文主要记录SVNServer从windows系列迁移到Centos的过程,以下几篇文章已经描述的很好了,本文就不再赘述. 注意问题: 配置权限时空格问题:包括前面不能有空格,=两边有空格等. 参考链 ...
- MVVM架构~knockoutjs系列之为Ajax传递Ko数组对象
返回目录 一些要说的 这是一个很有意思的题目,在KO里,有对象和数组对象两种,但这两种对象对外表现都是一个function,如果希望得到他的值,需要进行函数式调用,如ko_a(),它的结果为一个具体值 ...
- JavaScript模板引擎实例应用
在之前的一篇名为<移动端基于HTML模板和JSON数据的JavaScript交互>的文章中,我向大家说明了为什么要使用JavaScript模板以及如何使用,文末还提到了laytpl.art ...