array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 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 = 0, sub; sub = subs[i]; ++i)
      if(sub === term) return Boolean(this.subs.splice(i, 1))
  }
  /*
   * 分发新闻
   */
  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 = 0 } subTo(newsType){ this.subjects.push(newType) } unsubFrom(newsType){ for(let i = 0, n; n = this.subjects[i]; ++i) if(n === newsType) return this.subjects.splice(i, 1) } // 拉数据 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 = 100, ceiling = 5){
  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(0, 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

相关文章: