欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript编程设计模式之观察者模式(Observer Pattern)实例详解

程序员文章站 2022-04-28 23:17:24
本文实例讲述了javascript编程设计模式之观察者模式。分享给大家供大家参考,具体如下: 简介 简单的解释观察者模式,就是一个对象(subject)维护一个依赖他的...

本文实例讲述了javascript编程设计模式之观察者模式。分享给大家供大家参考,具体如下:

简介

简单的解释观察者模式,就是一个对象(subject)维护一个依赖他的对象(observers)列表,当自身状态发生变化时,自动通知所有观察者对象。当某个对象不需要获得通知时,可以从对象列表中删除掉。

从上面的解释中我们可以提炼出三个componet: subject, observerlist和observer,用js实现很简单:

function observerlist(){
 this.observerlist = [];
}
observerlist.prototype.add = function( obj ){
 return this.observerlist.push( obj );
};
observerlist.prototype.empty = function(){
 this.observerlist = [];
};
observerlist.prototype.count = function(){
 return this.observerlist.length;
};
observerlist.prototype.get = function( index ){
 if( index > -1 && index < this.observerlist.length ){
  return this.observerlist[ index ];
 }
};
observerlist.prototype.insert = function( obj, index ){
 var pointer = -1;
 if( index === 0 ){
  this.observerlist.unshift( obj );
  pointer = index;
 }else if( index === this.observerlist.length ){
  this.observerlist.push( obj );
  pointer = index;
 }
 return pointer;
};
observerlist.prototype.indexof = function( obj, startindex ){
 var i = startindex, pointer = -1;
 while( i < this.observerlist.length ){
  if( this.observerlist[i] === obj ){
   pointer = i;
  }
  i++;
 }
 return pointer;
};
observerlist.prototype.removeat = function( index ){
 if( index === 0 ){
  this.observerlist.shift();
 }else if( index === this.observerlist.length -1 ){
  this.observerlist.pop();
 }
};
// extend an object with an extension
function extend( extension, obj ){
 for ( var key in extension ){
  obj[key] = extension[key];
 }
}

subject拥有增加和删除observer的能力

function subject(){
 this.observers = new observerlist();
}
subject.prototype.addobserver = function( observer ){
 this.observers.add( observer );
};
subject.prototype.removeobserver = function( observer ){
 this.observers.removeat( this.observers.indexof( observer, 0 ) );
};
subject.prototype.notify = function( context ){
 var observercount = this.observers.count();
 for(var i=0; i < observercount; i++){
  this.observers.get(i).update( context );
 }
};

最后定义一个观察者对象,实现update方法

// the observer
function observer(){
 this.update = function(){
  // ...
 };
}

当有多个观察者,只需扩展上面的基本对象,并重写update方法。

尽管观察则模式被广泛使用,但在js中经常使用它的变体: 发布订阅模式

发布订阅模式通过一个topic/event通道,解耦了观察者模式中subject(发布者)和observer(订阅者)之间耦合的问题,在js中被广泛使用。

下面简单的例子说明了使用发布订阅模式的基本结构

// a very simple new mail handler
// a count of the number of messages received
var mailcounter = 0;
// initialize subscribers that will listen out for a topic
// with the name "inbox/newmessage".
// render a preview of new messages
var subscriber1 = subscribe( "inbox/newmessage", function( topic, data ) {
 // log the topic for debugging purposes
 console.log( "a new message was received: ", topic );
 // use the data that was passed from our subject
 // to display a message preview to the user
 $( ".messagesender" ).html( data.sender );
 $( ".messagepreview" ).html( data.body );
});
// here's another subscriber using the same data to perform
// a different task.
// update the counter displaying the number of new
// messages received via the publisher
var subscriber2 = subscribe( "inbox/newmessage", function( topic, data ) {
 $('.newmessagecounter').html( mailcounter++ );
});
publish( "inbox/newmessage", [{
 sender:"hello@google.com",
 body: "hey there! how are you doing today?"
}]);
// we could then at a later point unsubscribe our subscribers
// from receiving any new topic notifications as follows:
// unsubscribe( subscriber1, );
// unsubscribe( subscriber2 );

发布订阅模式的实现

许多js库都很好的实现了发布订阅模式,例如jquery的自定义事件功能。

// publish
// jquery: $(obj).trigger("channel", [arg1, arg2, arg3]);
$( el ).trigger( "/login", [{username:"test", userdata:"test"}] );
// dojo: dojo.publish("channel", [arg1, arg2, arg3] );
dojo.publish( "/login", [{username:"test", userdata:"test"}] );
// yui: el.publish("channel", [arg1, arg2, arg3]);
el.publish( "/login", {username:"test", userdata:"test"} );
// subscribe
// jquery: $(obj).on( "channel", [data], fn );
$( el ).on( "/login", function( event ){...} );
// dojo: dojo.subscribe( "channel", fn);
var handle = dojo.subscribe( "/login", function(data){..} );
// yui: el.on("channel", handler);
el.on( "/login", function( data ){...} );
// unsubscribe
// jquery: $(obj).off( "channel" );
$( el ).off( "/login" );
// dojo: dojo.unsubscribe( handle );
dojo.unsubscribe( handle );
// yui: el.detach("channel");
el.detach( "/login" );

简单实现

var pubsub = {};
(function(q) {
  var topics = {},
    subuid = -1;
  // publish or broadcast events of interest
  // with a specific topic name and arguments
  // such as the data to pass along
  q.publish = function( topic, args ) {
    if ( !topics[topic] ) {
      return false;
    }
    var subscribers = topics[topic],
      len = subscribers ? subscribers.length : 0;
    while (len--) {
      subscribers[len].func( topic, args );
    }
    return this;
  };
  // subscribe to events of interest
  // with a specific topic name and a
  // callback function, to be executed
  // when the topic/event is observed
  q.subscribe = function( topic, func ) {
    if (!topics[topic]) {
      topics[topic] = [];
    }
    var token = ( ++subuid ).tostring();
    topics[topic].push({
      token: token,
      func: func
    });
    return token;
  };
  // unsubscribe from a specific
  // topic, based on a tokenized reference
  // to the subscription
  q.unsubscribe = function( token ) {
    for ( var m in topics ) {
      if ( topics[m] ) {
        for ( var i = 0, j = topics[m].length; i < j; i++ ) {
          if ( topics[m][i].token === token) {
            topics[m].splice( i, 1 );
            return token;
          }
        }
      }
    }
    return this;
  };
}( pubsub ));

使用方法

// another simple message handler
// a simple message logger that logs any topics and data received through our
// subscriber
var messagelogger = function ( topics, data ) {
  console.log( "logging: " + topics + ": " + data );
};
// subscribers listen for topics they have subscribed to and
// invoke a callback function (e.g messagelogger) once a new
// notification is broadcast on that topic
var subscription = pubsub.subscribe( "inbox/newmessage", messagelogger );
// publishers are in charge of publishing topics or notifications of
// interest to the application. e.g:
pubsub.publish( "inbox/newmessage", "hello world!" );
// or
pubsub.publish( "inbox/newmessage", ["test", "a", "b", "c"] );
// or
pubsub.publish( "inbox/newmessage", {
 sender: "hello@google.com",
 body: "hey again!"
});
// we cab also unsubscribe if we no longer wish for our subscribers
// to be notified
// pubsub.unsubscribe( subscription );
// once unsubscribed, this for example won't result in our
// messagelogger being executed as the subscriber is
// no longer listening
pubsub.publish( "inbox/newmessage", "hello! are you still there?" );

更多关于javascript相关内容可查看本站专题:《javascript面向对象入门教程》、《javascript切换特效与技巧总结》、《javascript查找算法技巧总结》、《javascript错误与调试技巧总结》、《javascript数据结构与算法技巧总结》、《javascript遍历算法与技巧总结》及《javascript数学运算用法总结

希望本文所述对大家javascript程序设计有所帮助。