以【猫叫、老鼠跑、主人醒】为例子,使用 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都是一个简单的功能页面。这样就和实际情况比较接近了。

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

  1. //定义传播声音的介质 —— 空气
  2. var air = function () {
  3. var events = {
  4. SubjectEvent: [] //订阅者的注册事件,当有情况时触发这些事件
  5.  
  6. };
  7.  
  8. //添加订阅者的接收消息的事件。
  9. this.addSubjectEvent = function (e) {
  10. events.SubjectEvent.push(e);
  11. writeLog("[观察者接收了一个注册事件,接收事件数量:" + events.SubjectEvent.length + "。]<br/>");
  12. };
  13.  
  14. //监听声音。理论上是去监听,但是这里还是得被动调用。
  15. this.sendSound = function (info) {
  16. writeLog("[观察者开始传递声音,接收事件数量:" + events.SubjectEvent.length + "。]<br/>");
  17. Notify(info);
  18. }
  19.  
  20. //发出通知。
  21. var Notify = function (info) {
  22. //有发出声音的时候通知订阅者,就是遍历他们注册的事件
  23. for (var i = 0; i < events.SubjectEvent.length; i++) {
  24. var sound = "";
  25. if (typeof info.Sound != "undefined") {
  26. sound = info.Sound;
  27. }
  28.  
  29. writeLog("&nbsp;&nbsp;[观察者发出了一个通知 —— " + sound + "]<br/>");
  30. events.SubjectEvent[i](info);
  31. }
  32. }
  33. }

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

  1. var cat = function (name) {
  2. var name = name;
  3. this.sendSound;
  4.  
  5. //猫叫
  6. this.cry = function () {
  7. //发出声音
  8. parent.writeLog("<br/>" + name + "开始喵喵叫。<br/>");
  9. if (typeof this.sendSound != "undefined") {
  10. //触发事件
  11. parent.writeLog("声音传递了出去。<br/>");
  12. this.sendSound({ Sound: "喵喵叫", Volume: 3 });
  13.  
  14. }
  15. }
  16.  
  17. parent.writeLog("我是" + name + ",有人按俺就叫。<br/>");
  18. }

老鼠

  1. //可以看做是定义了一个类,当然并不准确
  2. var mouse = function (name) {
  3. var name = name; //可以看做是私有成员
  4. this.sendSound; //可以看做是公有成员
  5.  
  6. var _self = this;
  7.  
  8. //事件——老鼠跑
  9. this.run = function (info) {
  10. parent.writeLog(name + "听到了声音。<br/>");
  11. if (typeof info.Sound != "undefined") {
  12. //有声音,判断
  13. if (info.Sound == "逛逛跑") {
  14. //自己跑步的声音,不处理了,要不就死循环。
  15. parent.writeLog(name + "听到了自己的跑步声音。<br/>");
  16. }
  17. else {
  18. //其他声音,跑吧,不判断了。
  19. parent.writeLog(name + "开始狂飙。<br/>");
  20.  
  21. //发出声音
  22. if (typeof _self.sendSound != "undefined") {
  23. parent.writeLog(name + "狂飙发出了声音。<br/>");
  24. _self.sendSound({ Sound: "逛逛跑", Volume: 7 });
  25.  
  26. }
  27. }
  28.  
  29. };
  30. }
  31.  
  32. parent.writeLog("我是" + name + ",出来找吃的。<br/>");
  33.  
  34. }

主人

  1. var person = function (name) {
  2. var name = name;
  3. var isWark = false;
  4. this.sendSound;
  5.  
  6. //主人听到声音
  7. this.hearSound = function (info) {
  8.  
  9. if (isWark) {
  10. parent.writeLog(name + "已经醒了。<br/>");
  11. }
  12. else {
  13. if (typeof info.Volume != "undefined") {
  14. if (info.Volume <= 5) {
  15. parent.writeLog("声音小," + name + "继续睡觉。<br/>");
  16. }
  17. else if (info.Volume > 5) {
  18. parent.writeLog("声音大," + name + "被吵醒。<br/>");
  19. isWark = true;
  20. }
  21. }
  22.  
  23. }
  24. };
  25.  
  26. parent.writeLog("我是" + name + ",俺睡着了。<br/>");
  27. };

然后是实例化和调用

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

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

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

最后是开始表演

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

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

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

 
 
分类: 心情驿站

使用 javascript 来实现 观察者模式的更多相关文章

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

    “猫叫.老鼠跑.主人醒”是一个很古老的话题了,大家也都有各自的想法和解决方案.我也是看了很多,一开始的时候是相当的迷糊,这个怎么就是面试题了?考的是啥呀,和编程有关系吗?又是猫又是老鼠的,晕死了.后来 ...

  2. JavaScript设计模式之观察者模式(学习笔记)

    设计模式(Design Pattern)对于软件开发来说其重要性不言而喻,代码可复用.可维护.可扩展一直都是软件工程中的追求!对于我一个学javascript的人来说,理解设计模式似乎有些困难,对仅切 ...

  3. 再起航,我的学习笔记之JavaScript设计模式18(观察者模式)

    观察者模式 观察者模式(Observer): 又被称为发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合. 创建一个观察者对象 首先我们创建一个闭包对象,让其在页面加 ...

  4. JavaScript原生实现观察者模式

    观察者模式又叫做发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察着对象. 它是由两类对象组成,主题和观察者,主题负责发布事件 ...

  5. JavaScript设计模式-22.观察者模式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. javascript设计模式之观察者模式

    观察者模式又称发布/订阅模式   publish/subscribe 它是一种一对多的关系,让多个观察者对象同时监听某一主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得他们能够自动 ...

  7. javascript 设计模式之观察者模式

    观察者模式又叫发布——订阅模式,顾名思义pub——sub就是被动触发的,:不要给我......,我会给你.......就是一个发布订阅的解释,实质就是对程序中的某个对象状态进行监听观察,并且在该对象发 ...

  8. JavaScript设计模式(10)-观察者模式

    观察者模式 1. 介绍 发布者与订阅者是多对多的方式 通过推与拉获取数据:发布者推送到订阅者或订阅者到发布者那边拉 使并行开发的多个实现能彼此独立地进行修改 其实我们在前端开发中使用到的时间监听就是浏 ...

  9. js设计模式-观察者模式

    定义: 观察者模式又叫发布订阅模式,它定义了对象间的一种一对多的依赖关系.观察者模式让两个对象松耦合地联系在一起,虽然不太清楚彼此的细节,但这不影响他们之间的互相通信. 思路 定义一个对象,在对象中实 ...

随机推荐

  1. SSAS系列——【08】多维数据(程序展现Cube)

    原文:SSAS系列--[08]多维数据(程序展现Cube) 1.引用DLL? 按照之前安装的MS SQLServer的步骤安装完成后,发现在新建的项目中“Add Reference”时居然找不到Mic ...

  2. PHP-微信公众平台开发-接收用户输入消息类型并响应

    原文:PHP-微信公众平台开发-接收用户输入消息类型并响应 <?php // 该代码块用于接收用户消息,根据用户输入的消息类型进行判断,文本,图片,视频,位置,链接,语音等,并取得值,处理后给予 ...

  3. Swift入门教程:基本运算符

    基本运算符 Swift所支持的基本运算符 赋值运算符:= 复合赋值运算符:+=.-= 算数运算符:+.-.*./ 求余运算符:% 自增.自减运算符:++.-- 比较运算符:==.!=.>.< ...

  4. XML的序列化与反序列化

    开发时会把数据持久化成xml格式,当然可以用xmlwriter来实现,不过感觉不方便,而且很繁琐.推荐使用直接序列化.反序列化对象的方法来处理. 直接上代码: public static class ...

  5. OR导致笛卡尔积

    近期监控数据库,发现以下语句跑得很慢,原来运行计划走了导致笛卡尔积,来看以下语句: SQL> explain plan for 2 SELECT COUNT(*) 3 FROM "GD ...

  6. TDD(测试驱动开发)学习二:创建第一个TDD程序

    本节我们将学习一些测试驱动开发环境的搭建,测试驱动开发概念和流程.所涉及的内容全部会以截图的形式贴出来,如果你也感兴趣,可以一步一步的跟着来做,如果你有任何问题,可以进行留言,我也会很高兴的为你答疑. ...

  7. hdu 4858 项目管理(STL集装箱)

    项目管理 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  8. 查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究

    原文:查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究 查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究 -理解性能疑点 1      引言 内容来自http://www.so ...

  9. [转载]LVS快速搭建教程

    LVS配置教程 作者:oldjiang 一.前言 相信专程来读此文的读者对LVS必然有一定的了解,首先看图: 毋庸置疑,Load Balancer是负载调度器,由它将网络请求无缝隙调度到真实服务器,至 ...

  10. 利用自定义的AuthenticationFilter实现Basic认证

    [ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证   很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前 ...