一、题目介绍

以下是我copy自网上的面试题原文:

实现一个LazyMan,可以按照以下方式调用:

LazyMan("Hank")输出:

Hi! This is Hank!



LazyMan("Hank").sleep(10).eat("dinner")输出

Hi! This is Hank!

//等待10秒..

Wake up after 10

Eat dinner~



LazyMan("Hank").eat("dinner").eat("supper")输出

Hi This is Hank!

Eat dinner~

Eat supper~



LazyMan("Hank").sleepFirst(5).eat("supper")输出

//等待5秒

Wake up after 5

Hi This is Hank!

Eat supper



以此类推。

二、题目考察的点

先声明:我不是微信员工,考察的点是我推测的,可能不是,哈哈!

1.方法链式调用

 2.类的使用和面向对象编程的思路

 3.设计模式的应用

 4.代码的解耦

 5.最少知识原则,也即 迪米特法则(Law of Demeter)

 6.代码的书写结构和命名

三、题目思路解析

1.看题目输出示例,可以确定这是拟人化的输出,也就是说:应该编写一个类来定义一类人,叫做LazyMan。可以输出名字、吃饭、睡觉等行为。

 2.从输出的句子可以看出,sleepFrist的优先级是最高的,其他行为的优先级一致。

 3.从三个例子来看,都得先调用LazyMan来初始化一个人,才能继续后续行为,所以LazyMan是一个接口。

 4.句子是按调用方法的次序进行顺序执行的,是一个队列。

四、采用观察者模式实现代码

4.1 采用模块模式来编写代码

(function(window, undefined){

})(window);

4.2 声明一个变量taskList,用来存储需要队列信息

(function(window, undefined){
var taskList = [];
})(window);

队列中,单个项的存储设计为一个json,存储需要触发的消息,以及方法执行时需要的参数列表。比如LazyMan('Hank'),需要的存储信息如下。

{
'msg':'LazyMan',
'args':'Hank'
}

当执行LazyMan方法的时候,调用订阅方法,将需要执行的信息存入taskList中,缓存起来。

 存储的信息,会先保留着,等发布方法进行提取,执行和输出。

4.3 订阅方法

订阅方法的调用方式设计:subscribe("lazyMan", "Hank")

(function(window, undefined){
var taskList = []; // 订阅
function subscribe(){
var param = {},
args = Array.prototype.slice.call(arguments); if(args.length < 1){
throw new Error("subscribe 参数不能为空!");
} param.msg = args[0]; // 消息名
param.args = args.slice(1); // 参数列表 if(param.msg == "sleepFirst"){
taskList.unshift(param);
}else{
taskList.push(param);
}
}
})(window);

用一个param变量来组织好需要存储的信息,然后push进taskList中,缓存起来。

 特别的,如果是sleepFirst,则放置在队列头部。

4.4 发布方法

(function(window, undefined){
var taskList = []; // 订阅方法 代码... // 发布
function publish(){
if(taskList.length > 0){
run(taskList.shift());
}
}
})(window);

将队列中的存储信息读取出来,交给run方法(暂定,后续实现)去执行。这里限定每次发布只执行一个,以维持队列里面的方法可以挨个执行。

 另外,这里使用shift()方法的原因是,取出一个,就在队列中删除这一个,避免重复执行。

4.5 实现LazyMan类

// 类
function LazyMan(){}; LazyMan.prototype.eat = function(str){
subscribe("eat", str);
return this;
}; LazyMan.prototype.sleep = function(num){
subscribe("sleep", num);
return this;
}; LazyMan.prototype.sleepFirst = function(num){
subscribe("sleepFirst", num);
return this;
};

将LazyMan类实现,具有eat、sleep、sleepFrist等行为。

 触发一次行为,就在taskList中记录一次,并返回当前对象,以支持链式调用。

4.6 实现输出console.log的包装方法

// 输出文字
function lazyManLog(str){
console.log(str);
}

为什么还要为console.log包装一层,是因为在实战项目中,产经经常会修改输出提示的UI。如果每一处都用console.log直接调用,那改起来就麻烦很多。

 另外,如果要兼容IE等低级版本浏览器,也可以很方便的修改。

 也就是DRY原则(Don't Repeat Youself)。

4.7 实现具体执行的方法

// 具体方法
function lazyMan(str){
lazyManLog("Hi!This is "+ str +"!"); publish();
} function eat(str){
lazyManLog("Eat "+ str +"~");
publish();
} function sleep(num){
setTimeout(function(){
lazyManLog("Wake up after "+ num); publish();
}, num*1000); } function sleepFirst(num){
setTimeout(function(){
lazyManLog("Wake up after "+ num); publish();
}, num*1000);
}

这里的重点是解决setTimeout执行时会延迟调用,也即线程异步执行的问题。只有该方法执行成功后,再发布一次消息publish(),提示可以执行下一个队列信息。否则,就会一直等待。

4.8 实现run方法,用于识别要调用哪个具体方法,是一个总的控制台

// 鸭子叫
function run(option){
var msg = option.msg,
args = option.args; switch(msg){
case "lazyMan": lazyMan.apply(null, args);break;
case "eat": eat.apply(null, args);break;
case "sleep": sleep.apply(null,args);break;
case "sleepFirst": sleepFirst.apply(null,args);break;
default:;
}
}

这个方法有点像鸭式辨型接口,所以注释叫鸭子叫

 run方法接收队列中的单个消息,然后读取出来,看消息是什么类型的,然后执行对应的方法。

4.9 暴露接口LazyMan,让外部可以调用

(function(window, undefined){
// 很多代码... // 暴露接口
window.LazyMan = function(str){
subscribe("lazyMan", str); setTimeout(function(){
publish();
}, 0); return new LazyMan();
};
})(window);

接口LazyMan里面的publish方法必须使用setTimeout进行调用。这样能让publish()执行的线程延后,挂起。等链式方法都执行完毕后,线程空闲下来,再执行该publish()

 另外,这是一个对外接口,所以调用的时候,同时也会new 一个新的LazyMan,并返回,以供调用。

五、总结

1. 好处

使用观察者模式,让代码可以解耦到合理的程度,使后期维护更加方便。

 比如我想修改eat方法,我只需要关注eat()LazyMan.prototype.eat的实现。其他地方,我都可以不用关注。这就符合了最少知识原则

2. 不足

LazyMan.prototype.eat这种方法的参数,其实可以用arguments代替,我没写出来,怕弄得太复杂,就留个优化点吧。

 使用了unshift和shift方法,没有考虑到低版本IE浏览器的兼容。

六、完整源码和线上demo

完整源码已经放在我的gitHub上

源码入口https://github.com/wall-wxk/blogDemo/blob/master/2017/01/22/lazyMan.html

demo访问地址https://wall-wxk.github.io/blogDemo/2017/01/22/lazyMan.html

 demo需要打开控制台,在控制台中调试代码。

七、番外

网上有人也实现了lazyMan,但是实现的方式我不是很喜欢和认同,但是也是一种思路,这里顺便贴出来给大伙看看。

如何实现一个LazyManhttp://web.jobbole.com/89626/



阅读原文:www.jianshu.com/p/f1b7cb456d37

微信LazyMan笔试题的深入解析和实现的更多相关文章

  1. Java中有关构造函数的一道笔试题解析

    Java中有关构造函数的一道笔试题解析 1.详细题目例如以下 下列说法正确的有() A. class中的constructor不可省略 B. constructor必须与class同名,但方法不能与c ...

  2. 深度解析Objective-C笔试题

    2011-08-11 17:39 佚名 互联网 字号:T | T 本文介绍的是Objective-C笔试题,先来问一个,为什么很多内置类如UITableViewController的delegate属 ...

  3. 一道关于java 类初始化 成员初始化的笔试题的解析

    代码如下: java笔试题public class Mapplication { private static int n; private static Mapplication m1 = new ...

  4. 【第一期百题计划进行中,快来打卡学习】吃透java、细化到知识点的练习题及笔试题,助你轻松搞定java

    [快来免费打卡学习]参与方式 本期百题计划开始时间:2022-02-09,今日打卡题已在文中标红. 0.本文文末评论区打卡,需要登录才可以打卡以及查看其他人的打卡记录 1.以下练习题,请用对应的知识点 ...

  5. 也许你需要点实用的-Web前端笔试题

    之前发的一篇博客里没有附上答案,现在有空整理了下发出来,希望能帮助到正在找工作的你,还是那句话:技术只有自己真正理解了才是自己的东西,共勉. Web前端笔试题 Html+css 1.对WEB标准以及w ...

  6. js作用域之常见笔试题,运行结果题

    笔试题中经常有运行结果题,而大多体型都是围绕作用域展开,下面总结了几种相关的题: 外层的变量函数内部可以找到,函数内部的变量(局部变量)外层找不到. function aaa() { var a = ...

  7. C/C++ 笔试题

    /////转自http://blog.csdn.net/suxinpingtao51/article/details/8015147#userconsent# 微软亚洲技术中心的面试题!!! 1.进程 ...

  8. Java基础&笔试题

    这些题目是近期我参加过的笔试题和一些我在网上选的部分题,在这里做笔记,认真去学习,更好的应对后面的招聘.有错误欢迎指出. 一.Java基础部分 1.指针在任何情况下都可进行>,<,> ...

  9. Web前端面试笔试题总结

    最近一段时间要毕业了,忙着找工作,见过不少笔试面试题,自己总结了一些加上网上找的一些整合了一下.答案暂时都东拼西凑出来了,但是还是先不发出来,一方面是答案并不是唯一的并且自己的答案不能保证对,另一方面 ...

随机推荐

  1. APPIUM安装与搭建Q&A

    APPIUM安装与搭建Q&A Q1:在线安装TESTNG插件时,出现安装失败,提示:Cannot complete the install because one or more requir ...

  2. HDU 2859 Phalanx

    简单二维dp.o(n^3)效率过的.不知道有没有o(n^2)的解法. 为了方便点,先左右交换一下. dp[i][j]表示以[i,j]为左上角的最大对称矩阵长度 那么dp[i][j]=min(Max,d ...

  3. LED调光,PFM即pulse frequence modulation

    PWM不是唯一的调制方式,可以PWM,也可以PFM,也可以混合调制. PWM即pulse width modulation的缩写,脉冲宽度调制,保持开关周期不变,调节开关导通时间. PFM即pulse ...

  4. 总结分享十大iOS开发者最喜爱的库

    该10大iOS开发者最喜爱的库由“iOS辅导团队”成员Marcelo Fabri组织投票选举而得,参与者包括开发者团队,iOS辅导团队以及行业嘉宾.每个团队都要根据以下规则选出五个最好的库:1)不能投 ...

  5. Spring+struts+ibatis(一)环境准备工作

    首先我们先了解几个jar包的作用和一些未曾见过的接口和类 xwork-2.0.7.jar XWork是一个标准的Command模式实现,并且完全从web层脱离出来.Xwork提供了很多核心功能:前端拦 ...

  6. 14、手把手教你Extjs5(十四)模块字段和Grid列的定义[2]

    model和columns生成好了,下面要修改一下Module.js和Grid.js中的代码,使其能够协同工作. /** * 一个模块的主控界面的容器,用来安放各个模块控件以及协调他们之间的关系 */ ...

  7. 用weka来做Logistic Regression

    1.首先下载安装weka http://www.cs.waikato.ac.nz/ml/weka/downloading.html 2.打开weka,选择第一项Explorer 3.准备数据集文件,在 ...

  8. STM32/GD32芯片信息(转)

    源:STM32/GD32芯片信息 因为需要自动适配芯片进行系统配置,所以我们有必要通过读取一些系统寄存器来获取必要信息.我们的代码需要兼容STM32F1/GD32F1/STM32F0/STM32F4 ...

  9. IOS开发-UI学习-使用UIImageView控件制作动画

    先添加40张tomcat的图片到资源列表中:名称为cat_eat0000.jpg到cat_eat0039.jpg. 1.定义所需控件 // 定义按钮,图片控件.可变数组对象 UIButton *act ...

  10. Django performance

    Reference: https://impythonist.wordpress.com/2016/02/21/building-high-performance-django-systems/ Th ...