重構老項目所悟
0x01
6月份的那個時候,剛進ThoughtWorks不久,工做上也沒有太多的事情,而後就天真的覺得在騷窩的節奏應該一直就是這樣的吧,因此,便給接下來的幾個月定了一些小目標,其中就包括整理github已提交代碼,由於github上的東西真的太老了。可沒想到接下來的幾個項目以及北京Nodejs社區的事情讓我基本沒啥時間來作這些小目標了..html
前兩天,正好前端入門級的朋友來找我取經,我就在個人repo裏面找了一個能跑起來且適合他的項目推薦給他。雖然我將這個項目給他了,可這樣的項目拿出去我是會臉紅的,因此改造開始了..前端
0x02
首先,我加上了README。README基本的功能是告訴來訪者這個項目作了什麼,以及如何工做的。固然一個比較完善的README通常會包括以下部分:node
- Installation
- Example/Usage/Quickstart/Getting Started
- Features
- FAQ
- API References/Docs/Community
- Tests
- contributing
- contributors
- License
差很少就這些了,而後剩下的通常是專屬該項目的部分了。jquery
gitignore
有了README以後,發現這個項目尚未.gitignore!gitignore但是保證項目乾淨的利器啊,因而立馬給加上了。gitignore怎麼用,已經寫過一篇文大概介紹了,有興趣能夠看看。這裏,我再說說,項目裏面,通常什麼東西是不該該被push的。中心思想就是,項目的外部依賴庫都是不該該被push到repository的。爲何呢?由於這種外部依賴庫都是與咱們項目沒有關係的獨立庫,咱們能夠把它們看做是工具庫裏面的一種工具,咱們的項目只是使用了該工具,可是這個工具的源代碼跟我們項目的源代碼是不該該在一個代碼倉庫的。正好,我此次重構的項目就是一個很好的例子。在這個項目裏面的dist文件夾裏面有着Bootstrap和jQuery的源碼,他們是應該被提交到這裏的。那麼咱們應該怎麼作呢?方法比較多,這裏介紹兩個。第一個,咱們如今的npm庫很強大了,基本該有的都有了,Bootstrap和jQuery都有,咱們能夠經過npm安裝它們,而後在須要用到的地方引用。第二,不想用npm,固然也能夠用bower這樣的工具來下載,這東西主要就是Web端的包管理工具,bower install 下載的package默認會安裝在bower_components文件夾下面,固然咱們也能夠經過.bowerrc文件來指定安裝的目錄。不過,在npm一統天下的今天,bower使用的場景真是愈來愈少了。ok,咱們來總結一下哪些文件是gitignore的常客吧。git
這個就很少作解釋了,裏面都是外部依賴庫github
這個通常是打包以後的資源文件夾,對於前端項目來講,這個文件夾內的東西通常是能夠直接丟到Nginx裏被代理了。web
這個通常是在運行項目測試覆蓋率的時候生成的文件夾,在裏面咱們能看到整個項目和各個文件的測試覆蓋率。typescript
這裏通常是項目運行時記錄的日誌文件npm
該文件夾爲咱們在使用typescript的時候所須要的各類類型的聲明的地方。ide
0x03
接下來就是源代碼了。代碼,在可用的前提下首先應該是整潔乾淨的,這樣纔會讓本身寫的舒心,讓他人看的省心。因此,我清理了註釋代碼,無效的文件以及debug的console。接下來,就是代碼質量了。畢竟過去快兩年了,看當年的東西以爲處處都是問題。
咱們先來看一段代碼,
1 function judge_exist_barcode(item, promote) {
2 var judge_bar;
3 _.each(promote, function (pro) {
4 judge_bar = _.find(pro.barcodes, function (p) {
5 if (p == item.barcode) {
6 return p;
7 }
8 });
9 });
10 return judge_bar != undefined
11 }
問題挺多的,咱一個一個的看。
函數名
首先,這個函數的名字是judge_exist_barcode,而後看到返回值是一個Boolean值,再結合兩個參數能夠判斷出這個方法的主要功能是要判斷傳入的參數的barcode是否能在另外一個對象中找到。當一個函數的返回值或者變量的值的類型是Boolean的時候,咱們通常傾向於用 is、has、should等詞來開頭,由於這樣更表意,好比,若是把這裏的judge若是換成has是否是就更好了。最後咱們在根據函數的功能給它取一個新的名字,has_promotional_barcode,這個名字是否是就棒多了啊 :)
邏輯
這裏的代碼邏輯比較簡單,一眼就能看出來是作了什麼。首先,promote是一個可迭代對象,使用underscore的each方法遍歷promote,而後在promote的每個子對象的barcodes中查找是否有和傳入參數對象item的barcode相等的值,若是有,就給預先定義好的一個標識judge_bar賦值。那麼,一句話總結一下這個功能,查詢item對象的barcode是否有在promote的子對象的barcodes中出現。清楚了邏輯以後,再看看代碼,便知道這個judge_bar變量名是必定有問題的,而後再看看each和find方法,可以看出做爲新手對underscore/lodash提供的接口不夠了解。
ps: 因此,這裏我建議,剛接觸這個工具庫的朋友能夠先把他們的文檔快速、完整的瀏覽一遍,大概知道了它提供了哪些工具方法,這樣在工做中遇到也可以快速查文檔來使用。這裏我推薦使用Lodash,爲何呢?由於lodash在必定程度上有更優秀的性能,提供更多的工具和更快的更新,若是你想了解更多,能夠本身嘗試測試他們,或者來這裏找找答案。
查詢一個值是否存在於另外一個對象中咱們能夠用.some或者.includes,前者是對可迭代對象的子對象進行匹配校驗,用於對象之類的匹配或者key值校驗,支持identity function;後者也是對可迭代對象的子元素進行校驗,不支持identity,用於value值校驗。再加上咱們對context的理解,原代碼就可以重構成這樣
1 function has_promotional_barcode(item, promotions) {
2 return _.some(promotions, function (promotion) {
3 return _.include(promotion.barcodes, item.barcode);
4 });
5 }
首先,行數的縮減是最直觀的感覺,其次,語義上沒有損失,能夠像閱讀課文同樣容易的來理解。那麼,重構到這裏就結束了嗎?
0x04
重構其實才剛剛開始。正確的重構方式,應該是先爲現有代碼加上測試。這樣,咱們纔可以安心的去重構,沒必要擔憂由於本身的重構而致使代碼的行爲與以前的有不一致狀況。因此,前面提到過的TDD,不就能夠抓起來了嘛 :)
ps: 新年新氣象[Yeah!]