幾個業務場景中的重構示例 請求順序依賴前端
在這種場景中,首先仍是業務的複雜度決定了代碼的複雜度。首先咱們來看一個在前端和node都有可能出現的一個簡單的例子:node
咱們有 A, B, C, D 四個請求獲取數據的函數(函數本身實現), C 依賴 B 的結果,D 依賴 ABC 的結果,最終輸出 D 的結果。編程
錯誤示例 雖然這個代碼是故意寫成這樣的,不過確實也有在一些初學者身上看到過。這份代碼仍是能正確給出結果的,可是寫法醜陋,回調地獄。若是後來人不進行重構,還有請求依賴,得繼續回調嵌套。性能太差,沒有考慮 A 和 B 其實是能夠併發的。設計模式
這裏介紹了一下最原始的 callback ... 中間你們能夠去回顧一下 整個 ES2015+ ,callback (async.js) --> Promise --> generator + co --> async + await 的進化過程。實際上是從原生的語法層面不斷去簡化和加強咱們對於異步的控制能力。數據結構
下面直接給目前階段原生提供的終極方案:基於 Promise + async/await架構
正確示例 咱們從新思考了一下上面的問題,理清楚了邏輯順序的依賴。而且用最新的語法。併發
使用 Promise.all 結合 async/await 的形式,考慮了併發和串行,寫法簡潔,達到了在示例要求下的最快方案。解決了無限嵌套的問題。這是跟隨語言進化自己帶給咱們能夠進行的優化。異步
但又不只僅如此。咱們將問題進行歸類 將 B,C 有依賴順序的請求,抽離出單獨的函數。讓他們去處理自身的邏輯。這個點咱們稍後再提。 折磨人的 if elseasync
可能存在下面一些問題 一、過多的嵌套 二、邏輯處理冗餘 三、沒有作好防護編程(錯誤處理函數式編程
直接來一個代碼例子,這是一個獲取背景顏色的方法,可是隨着業務的不斷變化,背景顏色的來源愈來愈多,在一些業務人員的處理下多是這樣的:
錯誤示例 相信你在讀上面的代碼的時候是極爲痛苦的,想要一目瞭然的知道最終會進入哪一個分支,基本不可能。因而基於下面兩個原則
- 合理的抽取成函數
- 錯誤優先返回
有了一個基礎版本的重構:
正確示例 能夠看到整個邏輯,通過了從新梳理。拆分紅了三個函數,子方法分別去處理對應層級的邏輯,由一個主方法負責調度。總體都變得一目瞭然了。
固然,在咱們基於上面的原則進行重構以後,這個代碼有沒有問題呢?固然有。能夠看到咱們這三個函數,都依賴了全局變量。函數自己就不純了。若是是全局的問題,仍是不易於排查。
咱們能夠將其修改成純函數,讓這一份代碼易於理解和測試。
以一個函數的修改成示例:咱們將 全局變量變成了參數,只須要在調用的時候,將全局變量傳入便可,可是這樣,咱們獲得了一個純函數。 爲何會在這裏特別強調這個點呢,其實在函數式編程中的一個最基礎的問題那就是純函數。只有這樣輸入輸出纔是可被觀測的,一個輸入必定會有一個輸出。也只有經過這樣的方式,才能讓系統中非純的函數愈來愈少。讓代碼變得更易於測試。
固然做爲咱們若是以重構的角度去思考的話,咱們還須要關注到這個點: 這裏的邏輯會將會 最後一個被匹配到的數據,設置爲 bgColor 。(咱們都知道 find indexOf 等基本都是從前匹配。)是否真的是業務的需求呢?
能夠看到將業務代碼寫好/重構的過程當中其實也是對業務邏輯和業務理解的再一次提高。
不管是抽取成函數仍是錯誤優先返回的設計,這其實也都是能夠解決這樣一個問題:能在不去讀懂全局的狀況下,瞭解某一個區域的細節邏輯,也就作到了讓代碼易於理解和修改。
... 這裏的代碼即使是通過這樣的重構後,依然有能夠考慮進一步優化的空間,好比函數與參數的命名,完整的測試用例等等,受限於文章篇幅,暫不展開說明。
一些代碼中可能存在的其餘問題
- 邏輯耦合在視圖層。
- a === 'a' && b ==='b' && c==='c' && d ==='d'? <div>...</div>:null
- 組件複用,函數複用,不封裝,代碼重複。
- 函數功能不單一,一個函數處理太多職責。且這些職責沒有任何關聯,可是都耦合在同一個區塊內。
- 參數列表混亂,有作好防護編程,不處理錯誤(接口錯誤,超時,重複提交等等
- 魔法數字,魔法字符串,且沒說明。
- 糟糕數據結構 / 糟糕命名 (其實上面的具體代碼示例也存在)
關於優化代碼的思想準備
首先來講一下爲何會說須要優化代碼?
- 技術追求。
- 公司要求,線上有系統在用。有用戶在用,不寫好出問題實際上苦的仍是本身。
- 團隊協做,我很差好寫,團隊成員其餘人也很差好寫,惡性循環苦的仍是本身。
- 快速迭代。系統須要不斷的增長新功能。必需要寫好代碼才能作到。
- 其餘人的見解,怕別人以爲本身技術能力差... xxxx....
那麼就會有下面這些要求:
- 易於理解系統的架構
- 易於理解系統的生命週期與執行流程
- 易於理解每個函數的做用
- 易於理解函數之間是如何調用與傳遞的(輸入輸出)
- 易於理解變量的含義,表達式的含義。
- 易於擴展...
最終實際上又回到了寫出來的代碼應該是 整潔的代碼,要使代碼易於理解/修改/測試。(這裏其實大部分時候,都隱含了一我的員協做的條件在裏面,因此,既要寫好代碼,又不能過分封裝,讓團隊其餘成員看不懂(固然若是確實有些人經驗不夠,那麼是他自身的問題,須要他本身去增強。))
一些建議
- 更加清晰的去了解業務,去思考可能的變化。思考和設計清楚再動手。
- 看一些開源項目與業界最佳實踐,明白什麼樣的是好代碼,什麼樣的是很差的代碼。
- 創建明白代碼雖然是給計算機運行的,但最終仍是人看的。不只僅是沒有 bug 就好了,這樣的心智模型。
- 創建業務與代碼質量同等重要的思考模型。避免由於時間致使的不得不這麼寫的代碼。
- 明白 code review 自己可能能發現和指出來一些問題,但最終的落實還的靠本身,不能變成形式,而是須要融合成自身的思考。
- 使用錯誤優先原則。儘量的讓出錯的先返回, 這樣後面就會獲得乾淨的代碼。(寫代碼的時候,不只僅正向,反向的判斷也須要思考)
- 合理的拆分紅獨立的函數。明確輸入輸出,錯誤處理等在函數內部的處理。(好比在一些場景中確實會存在大量邏輯判斷,首先就要思考在判斷內部的語句是否能被歸類與拆分出去)
- 對於多種狀態的判斷與組合,可使用 組合狀態表 (map表)狀態機等模式
- 學習設計模式與重構等相關知識。
- 重構!!只要你以爲這個地方有問題了,那就不要等到之後。之後每每就是不再。
結束
說到這可能會有一種戛然而止的感受。在這一篇文章裏面,咱們首先以兩個優化代碼的具體實例爲引子,讓你們明白了一些業務代碼的優化思路。在以後從列舉了一些其餘可能出現的錯誤,以及是優化代碼的思想準備和理論指導。其實都是但願你們可以在業務中去發現問題,再去思考如何解決問題,由於說了那麼多,到底能不把代碼寫好,仍是得靠本身。
做者:三省吾身