微信LazyMan筆試題的深入解析和實現

發表於2017-02-03

一、題目介紹

以下是我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 採用模組模式來編寫程式碼

4.2 宣告一個變數taskList,用來儲存需要佇列資訊

佇列中,單個項的儲存設計為一個json,儲存需要觸發的訊息,以及方法執行時需要的引數列表。比如LazyMan(‘Hank’),需要的儲存資訊如下。

當執行LazyMan方法的時候,呼叫訂閱方法,將需要執行的資訊存入taskList中,快取起來。
儲存的資訊,會先保留著,等釋出方法進行提取,執行和輸出。

4.3 訂閱方法

訂閱方法的呼叫方式設計:subscribe("lazyMan", "Hank")

用一個param變數來組織好需要儲存的資訊,然後push進taskList中,快取起來。
特別的,如果是sleepFirst,則放置在佇列頭部。

4.4 釋出方法

將佇列中的儲存資訊讀取出來,交給run方法(暫定,後續實現)去執行。這裡限定每次釋出只執行一個,以維持佇列裡面的方法可以挨個執行。
另外,這裡使用shift()方法的原因是,取出一個,就在佇列中刪除這一個,避免重複執行。

4.5 實現LazyMan類

將LazyMan類實現,具有eat、sleep、sleepFrist等行為。
觸發一次行為,就在taskList中記錄一次,並返回當前物件,以支援鏈式呼叫。

4.6 實現輸出console.log的包裝方法

為什麼還要為console.log包裝一層,是因為在實戰專案中,產經經常會修改輸出提示的UI。如果每一處都用console.log直接呼叫,那改起來就麻煩很多。
另外,如果要相容IE等低階版本瀏覽器,也可以很方便的修改。
也就是DRY原則(Don’t Repeat Youself)。

4.7 實現具體執行的方法

這裡的重點是解決setTimeout執行時會延遲呼叫,也即執行緒非同步執行的問題。只有該方法執行成功後,再發布一次訊息publish(),提示可以執行下一個佇列資訊。否則,就會一直等待。

4.8 實現run方法,用於識別要呼叫哪個具體方法,是一個總的控制檯

這個方法有點像鴨式辨型介面,所以註釋叫鴨子叫
run方法接收佇列中的單個訊息,然後讀取出來,看訊息是什麼型別的,然後執行對應的方法。

4.9 暴露介面LazyMan,讓外部可以呼叫

介面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/

相關文章