zoukankan      html  css  js  c++  java
  • 使用 javascript 来实现 观察者模式

    以【猫叫、老鼠跑、主人醒】为例子,使用 javascript 来实现 观察者模式 (有在线演示)

    2013-06-24 08:35 by 金色海洋(jyk)阳光男孩, 572 阅读, 4 评论, 收藏编辑

      “猫叫、老鼠跑、主人醒”是一个很古老的话题了,大家也都有各自的想法和解决方案。我也是看了很多,一开始的时候是相当的迷糊,这个怎么就是面试题了?考的是啥呀,和编程有关系吗?又是猫又是老鼠的,晕死了。后来遇到有人写就去凑凑热闹看几眼。但还是迷迷糊糊。后来学习了面向对象的知识,知道了接口、委托,然后又看了《大话设计模式》。

      这时候再回过头去看猫呀、老鼠什么的,才能看懂一点。为什么只是看懂一点呢?因为还是不知道这个东东和写代码有啥关系,呵呵。再再后来,在自己写代码的时候,为了解决问题而用了使用了接口;为了提高运行效率而采用js+ajax时,要解决“主页面”和子页面(iframe里的页面)的事件调用的问题。都解决了之后,才对观察者模式有了更深入的理解。再去看相关的文章才能看得懂。(好像我把顺序给弄反了呀)

      webform的时候,感觉观察者模式比较鸡肋,因为web是无状态的,客户端可以主动访问服务器端,但是服务器端却不能主动找到客户端。这个……太烦人了。webform是怎么解决的呢?每次访问的时候都重新注册一遍。这个效率呀,虽然好像影响不大,但是知道原理之后就是感觉不爽。

      学习js快一年了,越学习越是感觉js的强大,同时也感觉,如果用javascript来实现“猫叫、老鼠跑、主人醒”的话,是不是更易读一些呢?看C#代码的时候,轻则接口、重则委托,如果这些我都不熟悉那么我咋看?当然你可以怪我基础知识不扎实,呵呵。但是我想js能够更明确的表达出来观察者模式的意图吧。

      我的理解和大家的好像不大一样,猫叫,声音传递了出去,老鼠听到了开始跑,跑动的声音发出去了,主人被吵醒。那么谁是观察者呢?传递声音的介质 —— 空气!空气在观察哪里发出了声音,然后把声音传递出去,传递给订阅者。这个是很自然而然的事情吧,没有任何的牵强。

      如果说老鼠是观察者,他在主动观察猫是不是发出了声音,听到了就跑,这个还勉强说得过去。但是主人呢?按照这个逻辑来说,主人也是观察者,他在主动观察老鼠跑动的声音,听到了就会醒。这个就说不过去了。人在睡觉的时候还会去主动观察声音吗?士兵在执行任务的时候会这么做,但是睡不好觉的。为了睡个好觉是不会去主动观察的。那么为什么会被吵醒呢?不是说好了,是“吵”醒嘛,是被动接收的,就是说他是订阅者,订阅了消息。有消息告诉我,而不是主动观察消息。同理老鼠也是一样。

     

      好了言归正传,开始说代码实现

      因为是js的,所以需要先介绍一下页面结构,因为老鼠、猫、主人都是独立个体,可以不放在一个页面里,所以我就设置了一个页面,里面放了三个iframe,分别指向 老鼠页、猫页、主人页。另一个原因就是,我们在做后台管理的时候,一般也是先弄一个页作为主页,然后在里面放个树,在放个tab标签,然后动态开n个iframe,每个iframe都是一个简单的功能页面。这样就和实际情况比较接近了。

      先定义传播声音的介质 —— 空气

    复制代码
    //定义传播声音的介质 —— 空气
            var air = function () {
                var events = {
                    SubjectEvent: []        //订阅者的注册事件,当有情况时触发这些事件
    
                };
    
                //添加订阅者的接收消息的事件。
                this.addSubjectEvent = function (e) {
                    events.SubjectEvent.push(e);
                    writeLog("[观察者接收了一个注册事件,接收事件数量:" + events.SubjectEvent.length + "。]<br/>");
                };
    
                //监听声音。理论上是去监听,但是这里还是得被动调用。
                this.sendSound = function (info) {
                    writeLog("[观察者开始传递声音,接收事件数量:" + events.SubjectEvent.length + "。]<br/>");
                    Notify(info);
                }
    
                //发出通知。
                var Notify = function (info) {
                    //有发出声音的时候通知订阅者,就是遍历他们注册的事件
                    for (var i = 0; i < events.SubjectEvent.length; i++) {
                        var sound = "";
                        if (typeof info.Sound != "undefined") {
                            sound = info.Sound;
                        }
    
                        writeLog("&nbsp;&nbsp;[观察者发出了一个通知 —— " + sound + "]<br/>");
                        events.SubjectEvent[i](info);
                    }
                }
            }
    复制代码

    然后分别是猫、老鼠、和主人

    复制代码
    var cat = function (name) {
                 var name = name;
                 this.sendSound;
    
                 //猫叫
                 this.cry = function () {
                     //发出声音
                     parent.writeLog("<br/>" + name + "开始喵喵叫。<br/>");
                     if (typeof this.sendSound != "undefined") {
                         //触发事件
                         parent.writeLog("声音传递了出去。<br/>");
                         this.sendSound({ Sound: "喵喵叫", Volume: 3 });
    
                     }
                 }
    
                 parent.writeLog("我是" + name + ",有人按俺就叫。<br/>");
             }
    复制代码

    老鼠

    复制代码
     //可以看做是定义了一个类,当然并不准确
             var mouse = function (name) {
                 var name = name;   //可以看做是私有成员
                 this.sendSound;    //可以看做是公有成员
    
                 var _self = this;
    
                 //事件——老鼠跑
                 this.run = function (info) {
                     parent.writeLog(name + "听到了声音。<br/>");
                     if (typeof info.Sound != "undefined") {
                         //有声音,判断
                         if (info.Sound == "逛逛跑") {
                             //自己跑步的声音,不处理了,要不就死循环。
                             parent.writeLog(name + "听到了自己的跑步声音。<br/>");
                         }
                         else {
                             //其他声音,跑吧,不判断了。
                             parent.writeLog(name + "开始狂飙。<br/>");
    
                             //发出声音
                             if (typeof _self.sendSound != "undefined") {
                                 parent.writeLog(name + "狂飙发出了声音。<br/>");
                                 _self.sendSound({ Sound: "逛逛跑", Volume: 7 });
    
                             }
                         }
                        
                     };
                 }
    
                 parent.writeLog("我是" + name + ",出来找吃的。<br/>");
    
             }
    复制代码

    主人

    复制代码
     var person = function (name) {
                 var name = name;
                 var isWark = false;
                 this.sendSound;
    
                 //主人听到声音
                 this.hearSound = function (info) {
    
                     if (isWark) {
                         parent.writeLog(name + "已经醒了。<br/>");
                     }
                     else {
                         if (typeof info.Volume != "undefined") {
                             if (info.Volume <= 5) {
                                 parent.writeLog("声音小," + name + "继续睡觉。<br/>");
                             }
                             else if (info.Volume > 5) {
                                 parent.writeLog("声音大," + name + "被吵醒。<br/>");
                                 isWark = true;
                             }
                         }
                         
                     }
                 };
    
                 parent.writeLog("我是" + name + ",俺睡着了。<br/>");
             };
    复制代码

    然后是实例化和调用

    复制代码
    var myAir = new air();
    
            //显示消息
            function writeLog(msg) {
                
                document.getElementById("msg").innerHTML += msg;
            };
    复制代码
    复制代码
     var Tom = new cat("小猫咪汤姆");
    
             window.onload = function () {
                 Tom.sendSound = parent.myAir.sendSound;  //发出声音的事件
                 
             };
    复制代码
    复制代码
      var Jerry = new mouse("小老鼠杰瑞");
             var longtao = new mouse("可怜的龙套甲");
    
             window.onload = function () {
                 Jerry.sendSound = parent.myAir.sendSound;  //发出声音的事件
                 longtao.sendSound = parent.myAir.sendSound;  //发出声音的事件
    
                 //杰瑞很聪明,注意听声音,申请了一个订阅 —— 有声音俺就 run
                 parent.myAir.addSubjectEvent(Jerry.run);
    
                 //为了做对比,龙套甲就不能去申请了。
    
             };
    复制代码
    复制代码
     var Master = new person("主人");
    
             window.onload = function () {
                 //主人睡觉了,但是为了发生意外,还是申请一个订阅吧,要不然万一着火了我还呼呼呢。
                 parent.myAir.addSubjectEvent(Master.hearSound);
                 
             };
    复制代码

    javascript里没有接口和委托的概念,但是并不是说没有这些功能,而是说不用去定义,直接用就可以。

    比如 myAir.addSubjectEvent(),可以直接把一个“事件”(Master.hearSound)当作参数传递进去,非常方便。

    最后是开始表演

    <span onclick="Tom.cry()">点俺俺就喵喵叫</span>

     在线演示 (需要点一下第二个iframe里的“点俺俺就喵喵叫”,才会开始运行)

    ps:快速理解javascript 的一种方法。这个方法不准确,只是用于熟悉c#、但是不熟悉js的人可以快速入门用。

     
     
    分类: 心情驿站
  • 相关阅读:
    Go 实现一个简单的TCP服务端
    将博客搬至CSDN
    My solution for Git Client Error: Permission denied (publickey)
    The Key to final data
    Design Pattern
    Difference between TCP and UDP
    Oracle SQL自带函数整理
    Java JDBC Batch
    Javascript Date 判断输入日期是否正确
    Javascript 数字保留2位小数
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3152012.html
Copyright © 2011-2022 走看看