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

  这时候再回过头去看猫呀、老鼠什么的,才能看懂一点。为什么只是看懂一点呢?因为还是不知道这个东东和写代码有啥关系,呵呵。再再后来,在自己写代码的时候,为了解决问题而用了使用了接口;为了提高运行效率而采用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的人可以快速入门用。

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

  1. 观察者模式:猫叫鼠跑人醒(C#)

    本着菜鸟先飞的想法,决定把平时遇到的知识点记录下来,一来是加深理解,二来是方便以后自己查阅.当您看到我这篇文章的时候,觉得代码有哪里不足的地方,请多指教,谢谢.(命名不规范是为了加深自己的理解,多多见 ...

  2. 老鼠跑猫叫主人惊醒c++观察者模式实现

    这个题目算是比较经典的观察者模式了,老鼠作为一个Subject,主动发出跑的动作,紧跟着猫由于老鼠的跑而发出叫声,主人也被惊醒,在这里猫跟主人都是被动的,是观察者角色,代码实现如下: class CS ...

  3. CTF---Web入门第七题 猫抓老鼠

    猫抓老鼠分值:10 来源: 实验吧 难度:难 参与人数:8697人 Get Flag:3740人 答题人数:3944人 解题通过率:95% catch!catch!catch!嘿嘿,不多说了,再说剧透 ...

  4. 实验吧 WEB 猫抓老鼠

    人生的第一道CTF题目哇,鸡冻 其实只是学了一下HTTP抓包得到的都是什么,就开始上手胡搞了 题目名字叫猫抓老鼠,还疯狂暗示catch!catch!catch!catch!,就想到要用抓包其实我是因为 ...

  5. UVALive 7308 Tom and Jerry 猫抓老鼠 物理题

    题目链接: 就是一个老鼠在环上一速度v开始绕环走,一只猫从圆心出发,任意时刻圆心,猫,老鼠三者在一条直线上,且速度也是v,求多久后猫抓到老鼠. #include <cstdio> #inc ...

  6. 面试习题之设计模式 C#观察者模式(猫叫老鼠惊走主人醒)

    腾讯云测试|TEST Tencent Cloud /* * CatShout.cs */ using System; using System.IO; using System.Collections ...

  7. 用oop分析场景,写出代码。房间里,有人、猫、老鼠在睡觉,然后猫醒了发出叫声,叫声惊醒了人,人从床上坐起来,惊醒了老鼠,老鼠开始逃跑。

    首先分析有哪些类: 应该有房子.动物类.人类.猫类.老鼠类. 房子不仅仅是一个容器,因为猫在房子里叫,惊醒了人和老鼠,所以猫叫是一个事件,通过这个事件触发人和老鼠的惊醒. 可以定义一个委托,利用委托绑 ...

  8. [Swift]LeetCode913.猫与老鼠 | Cat and Mouse

    A game on an undirected graph is played by two players, Mouse and Cat, who alternate turns. The grap ...

  9. 实验吧—Web——WP之 猫抓老鼠

    打开解题链接后,发现他是让我们输入key提交查询就能进行下一步 题目中提示了:catch(抓的意思) 那么我们很容易就能想到是BP抓包,抓包后直接放到repeater中改包. 我们先GO一下看看有什么 ...

随机推荐

  1. LigerUI学习使用

    LigerUI有如下主要特点: 使用简单,轻量级 控件实用性强,功能覆盖面大,可以解决大部分企业信息应用的设计场景 快速开发,使用LigerUI可以比传统开发减少极大的代码量 易扩展,包括默认参数.表 ...

  2. ASP.NET中使用DataGrid控件按照条件显示GridView单元格的颜色

    问题描述: 我在做一个关于信用卡管理系统时遇到一个问题:信用卡内金额低于100元时,数字颜色显示为红色,其余显示为绿色 之前,尝试了修改成为模板列以及转换成Reapeater控件,甚至用了Jquery ...

  3. Android入门(六)碎片

    原文链接:http://www.orlion.ga/493/ 一.碎片 碎片(Fragment)是一种可以嵌入在活动当中的 UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用的非常 ...

  4. Orlion个人博客 | 全栈工程师之路-www.orlion.ga

    自己用wordpress+bootstrap搭建了一个博客,网址http://www.orlion.ga,用来记录博主学习和成长,主要关注JAVA.LANMP.前端与客户端(Android).算法与数 ...

  5. sql基础语句大杂烩

    (坑Open Office,这排版...) 1.distinct列出不同值,过滤掉相同的值 例:company中有两个相同的值比如(apple和apple)时,则只取出一个值 SELECT DISTI ...

  6. 深入理解CSS背景

    前面的话 背景和字体一样,是一个复合属性,而且它是一个使用频率很高的属性.在CSS3中,背景属性在保持以前用法的同时,增加了新的相关属性.本文将详细介绍关于背景的知识 背景颜色 背景色backgrou ...

  7. mysql load data infile的使用 和 SELECT into outfile备份数据库数据

    LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name.txt' [REPLACE | IGNORE] INTO TABLE t ...

  8. Oracle IF & CASE语句

    IF语句主要有以下三种基本形式: 一. IF-THEN语句 IF CONDITION THEN           STATEMENT 1;           ...           STATE ...

  9. [转载]UML用例图总结

    前言 用例图主要用来描述“用户.需求.系统功能单元”之间的关系.它展示一个外部用户能够观察到的系统功能模型图.用例图多用于静态建模阶段(主要是业务建模和需求建模),帮助开发团队以一种可视化的方式理解系 ...

  10. SWFUpload简介及中文参考手册(share)

    SWFUpload SWFUpload 版本 2 概览 (Overview) 入门( Getting Started) js对象 (SWFUpload JavaScript Object) 构造器(C ...