瓜籽儿的Blog

专注于JavaScript技术!努力用最简单的办法去解决最复杂的问题!

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaScript设计模式之观察者模式

Posted on 2010-04-15 09:20  瓜籽  阅读(277)  评论(0编辑  收藏  举报

    关于JavaScript的观察者模式已经有很多的牛人做过详细的描述了,如:司图正美李平(Leepy)。Leepy的博客中对关于JavaScript的设计模式讲述的比较全面,如果大家有兴趣可以去拜读一下。

    我对观察者模式只有一个粗浅的认识,还没有达到牛人的境界。对写这些技术性的文章不太在行总有些力不从心,如果有哪些地方写的不好欢迎各位拍砖;关于“观察者模式”我也是在网上和书里看了好久才对他有点儿了解,下面就把我个人的一点总结写出来。一来可以做为一个备用,二来也希望可以和各位牛人们多多学习一下。 ^_^

    我喜欢把东西比喻的实物化一些,这样有助于记忆。我把“观察者模式”比喻成:将军和士兵的关系。这个怎么说呢?“将军”就好比是一位发布指令的“发布者”;士兵就好比是接收这些指令的“订阅者”。将军不可能只有一位,他们都可能会成为这个士兵的“发布者”。当将军做出一些决定(状态的改变)的时候就会将这些改变传递给士兵,让士兵按着将军的意图来完成。在现实生活中也会有这种事情发生,如上级给下级发派任务或是完成女友下达的任务,这些都是强制性的不能找任何的借口或是怨言,否则后果自负……T_T!

 

    下面就来看看将军和士兵分别在观察者模式中都是如何定义的。

    一、订阅者的定义,也就是士兵是如何接收指令的

         “订阅者”就是要接收上级下达下来的指令,但他们不能胡乱接收,得有一套约定好的规矩,俗话说无规矩不成方圆嘛。那这样一来就需要有一个用来接收指令的接口(这个接口已经由上级批准且备案了的)。一些资料中都将这些接口定义为“update()”,我这里也姑且还延用这个方法名了。

// 订阅者接口

function Subscribers(){};
    Subscribers.prototype = {
        update : function(){
            alert('订阅者接口'); // 默认接口内容,在以后定义中进行重定义
    }
};

    这个“订阅者接口”中定义了将要从上级下来的指令是通过“update()”方法来接收并做处理的。这只是一个原始状态还不能起到应有的功能,需要在后续的过程(以后所有的订阅者都会继承这个接口中的方法,也就是说只要是订阅者都会有这个update()方法来接收命令)中将其再一次重写来完成相应的功能。

    二、订阅者完成了,现在就开始制定将军下发指令时的功能接口了

// 发布者接口

function Subject(){
    this.observers = [];
};

Subject.prototype = {
    notice : function(content){
        for(var i = 0, len = this.observers.length; i < len; i++){
            this.observers[i].update(content);
        }
    },

    addObservers : function(observers){
        this.observers.push(observers);
    },

    removeObservers : function(observers){
        var index = this.observers.indexOf(observers);
        this.observers.slice(index, 1);
    }
};

    将军下达命令要包括以下这几个基本的功能:

        1、目前都有谁来受我指挥,也就是说订阅者都有谁?这就需要一个容器来存储这些订阅者,在“发布者接口”中用来存储这些订阅者的容器是一个数组(this.observers=[])。

        2、订阅者有了那就需要发挥将军的职能了----发布指令。方法“notice()”就是用来向订阅他指令的士兵发送命令的,通过这个方法将军向士兵中已经约定好的接口(update()方法)发布指令,针对于程序中就是将军在这个notice()方法中调用每一个订阅他指令的士兵的update()方法来下达命令。

        3、也可以有一个删除订阅者的方法。用以将不需要下达指令的士兵从订阅容器中清除。如程序中的removeObservers()方法就是来完成这个功能的。

     三、订阅者接口与发布者接口都已经定义完成了,现在我们就来真正的制定一个用来接收指令的士兵

        目前这个将军有2个士兵用以调遣,其中一个是用来显示下达指令后的提示信息;另一个是用来改变颜色。

var lis = document.getElementsByTagName('li');
		
// 我是一个兵,用来改变订阅者中的某个状态(弹出信息)

var MsgInfo = function(subject){
    this.subject = subject;
    this.subject.addObservers(this);
};

//继承方法

extend(MsgInfo, Subscribers);
		
MsgBox.prototype = {
    update : function(content){
        for(var i = 0, len = lis.length; i < len; i++){
            lis[i].onclick = (function(index){
                return function(){
                    alert('我的索引号是第:'+(index+1)+' ,而且还有相同的显示信息:'+content);
                }
            })(i);
        }
    }
};

		
// 我也是一个兵,用来改变订阅者中的某个状态(字体颜色)

var MsgStyle = function(subject){
    this.subject = subject;
    this.subject.addObservers(this);
};

extend(MsgStyle, Subscribers);
		
MsgStyle.prototype = {
    update : function(){
        for(var i = 0, len = lis.length; i < len; i++){
            lis[i].onmouseout = function(){
                this.style.color = 'red';
            };
        }
    }
};

    四、现在就需要将士兵与将军绑定在一起来完成下达与接受的功能了

        1、家有千口,主事一人。首先要让将军闪亮登场,他是这个台戏的主要的命令发布者,即实例化发布者。

        2、各位士兵排除迎接,即实例化士兵。

        3、将军与他的士兵们相互认识一下,建立合作关系,即绑定发布者与订阅者。

        4、将军发布命令,士兵执行。

// 将军闪亮登场,实例化发布者
var ImplementSubject = new Subject();

// 士兵排除欢迎,实例化订阅者。同时与发布者相互认识并建立合作关系
var box = new MsgInfo(ImplementSubject);
var boxStyle = new MsgStyle(ImplementSubject);

// 将军发布指令,通过notice()来执行订阅者的update()方法
ImplementSubject.notice('我是订阅者!');

    以上就完成了一个“发布者”与“订阅者”的模拟关系,我们可以通过这种模式来完成一些(一对多)功能。通过执行某一个元素的功能来完成其他元素状态的改变,从而达到了“发布者”和“订阅者”的关系。下面是一个完整的例子。