代码改变世界

javascript观察者模式

2010-02-24 15:28  BlueDream  阅读(345)  评论(0编辑  收藏  举报

观察者模式(Observer).也被称作发布者-订阅者(publisher-subscriber)模式.

这里用个实际中的例子说明.比如报纸行业,这里面有两个主要角色:发行商 和 订阅者.

整个报纸的流程就是:

当新的报纸出现.发行商会通过信息告诉订阅者,最新的报纸已经发行了.请注意订阅.那么订阅者收到这个信息就会做出反应.或者订阅,然后进行一系列的活动(如:查询信息,剪报,给朋友讲解报纸新闻).也可以退订等.

当然发行商和订阅者的沟通方式不是唯一的[如推,拉两种方式].

推: 发行商由于公司规模较大,人员充足,就选择推得方式,用人工挨家挨户的送报纸.比较直接.

拉: 发行商规模较小,只能将报纸放到固定地点,如果有人需要订阅可以去固定地点去订阅.

[简述]

观察者模式就是定义了对象间的一对多关系(一个发行商 对 多个订阅者) 或 多对多关系(一个订阅者 对 多家发行商).

然后当发行商对象的状态改变了(报纸已经印刷完毕),那么订阅者对象就会获取到相关信息而做出对应的反应.

[实例]

多选框全选

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title> AllCheck </title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 </head>
 <body>
 <script type="text/javascript">
 /*!
    观察者模式(Observer)
 */
 function ArrayList() {
    this.aList = [];
 }

 ArrayList.prototype.Count = function() {
    return this.aList.length;
 }

 ArrayList.prototype.Add = function(object) {
    return this.aList.push(object);
 }

 ArrayList.prototype.GetAt = function(index) {
    if(index > -1 && index < this.aList.length) {
        return this.aList[index];
    } else {
        return undefined;
    }
 }

 ArrayList.prototype.Clear = function() {
    this.aList = [];
 }

 ArrayList.prototype.RemoveAt = function(index) {
    var m_count = this.aList.length;
    if(m_count > 0 && index > -1 && index < this.aList.length) {
        switch(index) {
            case 0:
                this.aList.shift();
                break;
            case m_count - 1:
                this.aList.pop();
                break;
            default:
                var head = this.aList.slice(0, index);
                var tail = this.aList.slice(index + 1);
                this.aList = head.concat(tail);
                break;
        }
    }
 }

 ArrayList.prototype.Insert = function(object, index) {
    var m_count = this.aList.length;
    var m_returnValue = -1;
    if(index > -1 && index <= m_count) {
        switch(index) {
            case 0:
                this.aList.unshift(object);
                m_returnValue = 0;
                break;
            case m_count:
                this.aList.push(object);
                m_returnValue = m_count;
                break;
            default:
                var head = this.aList.slice(0, index - 1);
                var tail = this.aList.slice(index);
                this.aList = this.aList.concat(tail.unshift(object)); // ??
                m_returnValue = index;
                break;
        }
    }
    return m_returnValue;
 }

 ArrayList.prototype.IndexOf = function(object, startIndex) {
    var m_count = this.aList.length;
    var m_returnValue = -1;
    if(startIndex > -1 && startIndex < m_count) {
        var i = startIndex;
        while( i < m_count ) {
            if(this.aList[i] == object) {
                m_returnValue = i;
                break;
            }
            i++;
        }
    }
    return m_returnValue;
 }

 ArrayList.prototype.LastIndexOf = function(object, startIndex) {
    var m_count = this.aList.length;
    var m_returnValue = -1;
    if(startIndex > -1 && startIndex < m_count) {
        var i = m_count - 1;
        while(i >= startIndex) {
            if(this.aList[i] == object) {
                m_returnValue - i;
                break;
            }
            i--;
        }
    }
    return m_returnValue;
 }

 /* 观察者 */
 function Observer() {
    this.Update = function() {
        return;
    }
 }
 /* 发送者 */
 function Subject() {
    this.observers = new ArrayList();
 }
 /* 发送通知 */
 Subject.prototype.Notify = function(context) {
    var m_count = this.observers.Count();
    for(var i = 0; i < m_count; i++) {
        this.observers.GetAt(i).Update(context);
    }
 }
 /* 添加观察者 */
 Subject.prototype.AddObserver = function(observer) {
    if(!observer.Update) { throw 'Wrong observer'; }
    this.observers.Add(observer);
 }
 /* 移除观察者 */
 Subject.prototype.RemoveObserver = function(observer) {
    if(!observer.Update) { throw 'Wrong observer'; }
    this.observers.RemoveAt(this.observers.IndexOf(observer, 0));
 }

 /* Copy元类继承 */
 function inherits(base, extension) {
    for(var property in  base) {
        try {
            extension[property] = base[property];
        }
        catch(warning) {}
    }
 }
 
 /***************** Concrete Subject ***********************/
 var mainCheck = document.createElement("INPUT");
 mainCheck.type = 'checkbox';
 mainCheck.id = 'MainCheck';
 mainCheck.style.border = '1px solid red';
 inherits(new Subject(), mainCheck);
 /* 当点击按钮的时候 给相关的观察者发送通知. 观察者接收到通知的时候 改变状态 */
 mainCheck['onclick'] = new Function("mainCheck.Notify(mainCheck.checked)");
 document.body.appendChild(mainCheck);

 /********************* Observer *****************************/

 var obsCheck1 = document.createElement('INPUT');
 var obsCheck2 = document.createElement('INPUT');
 obsCheck1.type = 'checkbox';
 obsCheck1.id = 'Obs1';
 document.body.appendChild(obsCheck1);

 obsCheck2.type = 'checkbox';
 obsCheck2.id = 'Obs2';
 document.body.appendChild(obsCheck2);
 inherits(new Observer(), obsCheck1);
 inherits(new Observer(), obsCheck2);

 /* 观察者改变状态 */
 obsCheck1.Update = function(value) {
    this.checked = value;
 }

 obsCheck2.Update = function(value) {
    this.checked = value;
 }

 // 将发行者和订阅者(观察者)关联
 mainCheck.AddObserver(obsCheck1);
 mainCheck.AddObserver(obsCheck2);
 </script>
 </body>
</html>

流程:

两个角色Subject [第一个checkbox]和Observer[其余两个checkbox].

ID为MainCheck的第一个checkbox继承Subject 即文中提到的发布者.其余的两个ID分别为Obs1,Obs2的两个checkbox继承Observer为订阅者.

当点击MainCheck复选框的时候.发布者就会向观察者发布消息(这里发布的是他的选中状态). 观察者收到信息后就改变成对应的状态.而实现了全选控制.