<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          垃圾代碼和優(yōu)質(zhì)代碼的區(qū)別

          共 6585字,需瀏覽 14分鐘

           ·

          2021-11-30 00:08

          點(diǎn)擊下方“IT牧場(chǎng)”,選擇“設(shè)為星標(biāo)”

          點(diǎn)擊下方“IT牧場(chǎng)”,選擇“設(shè)為星標(biāo)”


          來(lái)源:zhuanlan.zhihu.com/p/99246269

          • 幾個(gè)業(yè)務(wù)場(chǎng)景中的重構(gòu)示例
            • 請(qǐng)求順序依賴
            • 錯(cuò)誤示例
            • 正確示例
            • 折磨人的 if else
            • 錯(cuò)誤示例
            • 正確示例
          • 一些代碼中可能存在的其他問(wèn)題
          • 關(guān)于優(yōu)化代碼的思想準(zhǔn)備
          • 一些建議
          • 結(jié)束

          幾個(gè)業(yè)務(wù)場(chǎng)景中的重構(gòu)示例

          請(qǐng)求順序依賴

          在這種場(chǎng)景中,首先還是業(yè)務(wù)的復(fù)雜度決定了代碼的復(fù)雜度。首先我們來(lái)看一個(gè)在前端和node都有可能出現(xiàn)的一個(gè)簡(jiǎn)單的例子:

          我們有 A, B, C, D 四個(gè)請(qǐng)求獲取數(shù)據(jù)的函數(shù)(函數(shù)自己實(shí)現(xiàn)), C 依賴 B 的結(jié)果,D 依賴 ABC 的結(jié)果,最終輸出 D 的結(jié)果。

          錯(cuò)誤示例

          //?偽代碼
          function?A(callbak)?{
          ??ajax(url,?function(res)?{
          ????callbak(res);
          ??});
          }
          //?...?剩下?同上
          //?實(shí)現(xiàn)如下:
          A(function(resa)?{
          ??B(function(resb)?{
          ????C(resb,?function(resc)?{
          ??????D(resa,?resb,?resc,?function(resd)?{
          ????????console.log("this?is?D?result:",?resd);
          ??????});
          ????});
          ??});
          });

          雖然這個(gè)代碼是故意寫成這樣的,不過(guò)確實(shí)也有在一些初學(xué)者身上看到過(guò)。這份代碼還是能正確給出結(jié)果的,但是寫法丑陋,回調(diào)地獄。如果后來(lái)人不進(jìn)行重構(gòu),還有請(qǐng)求依賴,得繼續(xù)回調(diào)嵌套。性能太差,沒有考慮 A 和 B 實(shí)際上是可以并發(fā)的。

          這里介紹了一下最原始的 callback ... 中間大家可以去回顧一下 整個(gè) ES2015+ ,callback (async.js) --> Promise --> generator + co --> async + await 的進(jìn)化過(guò)程。其實(shí)是從原生的語(yǔ)法層面不斷去簡(jiǎn)化和增強(qiáng)我們對(duì)于異步的控制能力。

          下面直接給目前階段原生提供的終極方案:基于 Promise + async/await

          正確示例

          function?A()?{
          ??return?new?Promise(r?=>
          ????setTimeout(()?=>?{
          ??????r("a");
          ????},?2000)
          ??);
          }
          //?...剩下同上
          async?function?asyncBC()?{
          ??const?resb?=?await?B();
          ??const?resc?=?await?c(resb);
          ??return?{?resb,?resc?};
          }
          async?function?asyncTask()?{
          ??const?[resa,?{?resb,?resc?}]?=?await?Promise.all([A(),?asyncBC()]);
          ??const?resd?=?await?D(resa,?resb,?resc);
          ??return?resd;
          }
          asyncTask().then(resd?=>?{
          ??console.log("this?is?D?result:",?resd);
          });

          我們重新思考了一下上面的問(wèn)題,理清楚了邏輯順序的依賴。并且用最新的語(yǔ)法。

          使用?Promise.all?結(jié)合?async/await?的形式,考慮了并發(fā)和串行,寫法簡(jiǎn)潔,達(dá)到了在示例要求下的最快方案。解決了無(wú)限嵌套的問(wèn)題。這是跟隨語(yǔ)言進(jìn)化本身帶給我們可以進(jìn)行的優(yōu)化。

          但又不僅僅如此。我們將問(wèn)題進(jìn)行歸類 將 B,C 有依賴順序的請(qǐng)求,抽離出單獨(dú)的函數(shù)。讓他們?nèi)ヌ幚碜陨淼倪壿?。這個(gè)點(diǎn)我們稍后再提。

          折磨人的 if else

          可能存在下面一些問(wèn)題

          1. 過(guò)多的嵌套
          2. 邏輯處理冗余
          3. 沒有做好防御編程(錯(cuò)誤處理

          直接來(lái)一個(gè)代碼例子,這是一個(gè)獲取背景顏色的方法,但是隨著業(yè)務(wù)的不斷變化,背景顏色的來(lái)源越來(lái)越多,在一些業(yè)務(wù)人員的處理下可能是這樣的:

          錯(cuò)誤示例

          const?_getPageBgColor?=?(pageInfo,?pageUrlObj)?=>?{
          ??let?bgColor?=?"";
          ??if?(window.__isMyCache)?{
          ????if?(pageInfo?&&?pageInfo.theme)?{
          ??????bgColor?=?pageInfo.theme.backgroundColor;
          ????}?else?{
          ??????if?(window.__customMyConfig.backgroundMap)?{
          ????????let?queryParam?=?pageUrlObj.params;
          ????????if?(queryParam.myPid)?{
          ??????????let?pids?=?queryParam.myPid.split("-");
          ??????????if?(pids.length?===?2)?{
          ????????????bgColor?=?window.__customMyConfig.backgroundMap[pids[1]];
          ??????????}?else?{
          ????????????bgColor?=?window.__customMyConfig.backgroundMap[pids[0]];
          ??????????}
          ????????}
          ??????}
          ??????if?(!bgColor?&&?window.__customMyConfig.customBgMap)?{
          ????????Object.keys(window.__customMyConfig.customBgMap).forEach(item?=>?{
          ??????????if?(this.pageUrl.indexOf(item)?>?0)?{
          ????????????bgColor?=?window.__customMyConfig.customBgMap[item];
          ??????????}
          ????????});
          ??????}
          ????}
          ??}?else?{
          ????if?(window.__pageTheme)?{
          ??????bgColor?=?window.__pageTheme.backgroundColor;
          ????}
          ??}
          ??return?bgColor;
          };

          相信你在讀上面的代碼的時(shí)候是極為痛苦的,想要一目了然的知道最終會(huì)進(jìn)入哪個(gè)分支,基本不可能。

          于是基于下面兩個(gè)原則

          • 合理的抽取成函數(shù)
          • 錯(cuò)誤優(yōu)先返回

          有了一個(gè)基礎(chǔ)版本的重構(gòu):

          正確示例

          const?getBackgroundMapBgColor?=?pageUrlObj?=>?{
          ??if?(!window.__customMyConfig.backgroundMap)?return?"";
          ??let?queryParam?=?pageUrlObj.params;
          ??if?(!queryParam.myPid)?return?"";
          ??const?pids?=?queryParam.myPid.split("-");
          ??if?(pids.length?===?2)?{
          ????return?window.__customMyConfig.backgroundMap[pids[1]];
          ??}
          ??return?window.__customMyConfig.backgroundMap[pids[0]];
          };
          const?getCustomBgMapBgColor?=?()?=>?{
          ??let?bgColor?=?"";
          ??if?(window.__customMyConfig.customBgMap)?return?"";
          ??Object.keys(window.__customMyConfig.customBgMap).forEach(item?=>?{
          ????if?(this.pageUrl.indexOf(item)?!==?-1)?{
          ??????bgColor?=?window.__customMyConfig.customBgMap[item];
          ????}
          ??});
          ??return?bgColor;
          };
          const?_getPageBgColor?=?(pageInfo,?pageUrlObj)?=>?{
          ??if?(!window.__isMyCache?&&?!window.__pageTheme)?return?"";
          ??if?(window.__pageTheme)?{
          ????return?window.__pageTheme.backgroundColor;
          ??}
          ??if?(pageInfo?&&?pageInfo.theme)?{
          ????return?pageInfo.theme.backgroundColor;
          ??}
          ??let?bgColor?=?getBackgroundMapBgColor(pageUrlObj);
          ??if?(!bgColor)?bgColor?=?getCustomBgMapBgColor();
          ??return?bgColor;
          };

          可以看到整個(gè)邏輯,經(jīng)過(guò)了重新梳理。拆分成了三個(gè)函數(shù),子方法分別去處理對(duì)應(yīng)層級(jí)的邏輯,由一個(gè)主方法負(fù)責(zé)調(diào)度。整體都變得一目了然了。

          當(dāng)然,在我們基于上面的原則進(jìn)行重構(gòu)之后,這個(gè)代碼有沒有問(wèn)題呢?當(dāng)然有。可以看到我們這三個(gè)函數(shù),都依賴了全局變量。函數(shù)本身就不純了。如果是全局的問(wèn)題,還是不易于排查。

          我們可以將其修改為純函數(shù),讓這一份代碼易于理解和測(cè)試。

          以一個(gè)函數(shù)的修改為示例:我們將 全局變量變成了參數(shù),只需要在調(diào)用的時(shí)候,將全局變量傳入即可,但是這樣,我們得到了一個(gè)純函數(shù)。

          const?getBackgroundMapBgColor?=?(pageUrlObj,?config)?=>?{
          ??if?(!config.backgroundMap)?return?"";
          ??let?queryParam?=?pageUrlObj.params;
          ??if?(!queryParam.myPid)?return?"";
          ??const?pids?=?queryParam.myPid.split("-");
          ??if?(pids.length?===?2)?{
          ????return?config.backgroundMap[pids[1]];
          ??}
          ??return?config.backgroundMap[pids[0]];
          };

          為什么會(huì)在這里特別強(qiáng)調(diào)這個(gè)點(diǎn)呢,其實(shí)在函數(shù)式編程中的一個(gè)最基礎(chǔ)的問(wèn)題那就是純函數(shù)。只有這樣輸入輸出才是可被觀測(cè)的,一個(gè)輸入一定會(huì)有一個(gè)輸出。也只有通過(guò)這樣的方式,才能讓系統(tǒng)中非純的函數(shù)越來(lái)越少。讓代碼變得更易于測(cè)試。

          當(dāng)然作為我們?nèi)绻灾貥?gòu)的角度去思考的話,我們還需要關(guān)注到這個(gè)點(diǎn):

          Object.keys(window.__customMyConfig.customBgMap).forEach(item?=>?{
          ????if?(this.pageUrl.indexOf(item)?!==?-1)?{
          ??????bgColor?=?window.__customMyConfig.customBgMap[item];
          ????}
          });

          這里的邏輯會(huì)將會(huì) 最后一個(gè)被匹配到的數(shù)據(jù),設(shè)置為 bgColor 。(我們都知道 find indexOf 等基本都是從前匹配。)是否真的是業(yè)務(wù)的需求呢?

          可以看到將業(yè)務(wù)代碼寫好/重構(gòu)的過(guò)程中其實(shí)也是對(duì)業(yè)務(wù)邏輯和業(yè)務(wù)理解的再一次提升。不論是抽取成函數(shù)還是錯(cuò)誤優(yōu)先返回的設(shè)計(jì),這其實(shí)也都是可以解決這樣一個(gè)問(wèn)題:能在不去讀懂全局的情況下,了解某一個(gè)區(qū)域的細(xì)節(jié)邏輯,也就做到了讓代碼易于理解和修改。

          ... 這里的代碼即便是經(jīng)過(guò)這樣的重構(gòu)后,依然有可以考慮進(jìn)一步優(yōu)化的空間,比如函數(shù)與參數(shù)的命名,完整的測(cè)試用例等等,受限于文章篇幅,暫不展開說(shuō)明。

          推薦下自己做的 Spring Boot 的實(shí)戰(zhàn)項(xiàng)目:

          https://github.com/YunaiV/ruoyi-vue-pro

          一些代碼中可能存在的其他問(wèn)題

          1. 邏輯耦合在視圖層。

            a === 'a' && b ==='b' && c==='c' && d ==='d'?

            ...
            :null

          2. 組件復(fù)用,函數(shù)復(fù)用,不封裝,代碼重復(fù)。

          3. 函數(shù)功能不單一,一個(gè)函數(shù)處理太多職責(zé)。且這些職責(zé)沒有任何關(guān)聯(lián),但是都耦合在同一個(gè)區(qū)塊內(nèi)。

          4. 參數(shù)列表混亂,有做好防御編程,不處理錯(cuò)誤(接口錯(cuò)誤,超時(shí),重復(fù)提交等等

          5. 魔法數(shù)字,魔法字符串,且沒說(shuō)明。

          6. 糟糕數(shù)據(jù)結(jié)構(gòu) / 糟糕命名 (其實(shí)上面的具體代碼示例也存在)

          推薦下自己做的 Spring Cloud 的實(shí)戰(zhàn)項(xiàng)目:

          https://github.com/YunaiV/onemall

          關(guān)于優(yōu)化代碼的思想準(zhǔn)備

          首先來(lái)說(shuō)一下為什么會(huì)說(shuō)需要優(yōu)化代碼?

          1. 技術(shù)追求。
          2. 公司要求,線上有系統(tǒng)在用。有用戶在用,不寫好出問(wèn)題實(shí)際上苦的還是自己。
          3. 團(tuán)隊(duì)協(xié)作,我不好好寫,團(tuán)隊(duì)成員其他人也不好好寫,惡性循環(huán)苦的還是自己。
          4. 快速迭代。系統(tǒng)需要不斷的增加新功能。必須要寫好代碼才能做到。
          5. 其他人的看法,怕別人覺得自己技術(shù)能力差... xxxx....

          那么就會(huì)有下面這些要求:

          • 易于理解系統(tǒng)的架構(gòu)
          • 易于理解系統(tǒng)的生命周期與執(zhí)行流程
          • 易于理解每一個(gè)函數(shù)的作用
          • 易于理解函數(shù)之間是如何調(diào)用與傳遞的(輸入輸出)
          • 易于理解變量的含義,表達(dá)式的含義。
          • 易于擴(kuò)展...

          最終實(shí)際上又回到了寫出來(lái)的代碼應(yīng)該是 整潔的代碼,要使代碼易于理解/修改/測(cè)試。(這里其實(shí)大部分時(shí)候,都隱含了一個(gè)人員協(xié)作的條件在里面,所以,既要寫好代碼,又不能過(guò)度封裝,讓團(tuán)隊(duì)其他成員看不懂(當(dāng)然如果確實(shí)有些人經(jīng)驗(yàn)不夠,那么是他自身的問(wèn)題,需要他自己去加強(qiáng)。))

          一些建議

          1. 更加清晰的去了解業(yè)務(wù),去思考可能的變化。思考和設(shè)計(jì)清楚再動(dòng)手。
          2. 看一些開源項(xiàng)目與業(yè)界最佳實(shí)踐,明白什么樣的是好代碼,什么樣的是不好的代碼。
          3. 建立明白代碼雖然是給計(jì)算機(jī)運(yùn)行的,但最終還是人看的。不僅僅是沒有 bug 就行了,這樣的心智模型。
          4. 建立業(yè)務(wù)與代碼質(zhì)量同等重要的思考模型。避免因?yàn)闀r(shí)間導(dǎo)致的不得不這么寫的代碼。
          5. 明白 code review 本身可能能發(fā)現(xiàn)和指出來(lái)一些問(wèn)題,但最終的落實(shí)還的靠自己,不能變成形式,而是需要融合成自身的思考。
          6. 使用錯(cuò)誤優(yōu)先原則。盡可能的讓出錯(cuò)的先返回, 這樣后面就會(huì)得到干凈的代碼。(寫代碼的時(shí)候,不僅僅正向,反向的判斷也需要思考)
          7. 合理的拆分成獨(dú)立的函數(shù)。明確輸入輸出,錯(cuò)誤處理等在函數(shù)內(nèi)部的處理。(比如在一些場(chǎng)景中確實(shí)會(huì)存在大量邏輯判斷,首先就要思考在判斷內(nèi)部的語(yǔ)句是否能被歸類與拆分出去)
          8. 對(duì)于多種狀態(tài)的判斷與組合,可以使用 組合狀態(tài)表 (map表)狀態(tài)機(jī)等模式。
          9. 學(xué)習(xí)設(shè)計(jì)模式與重構(gòu)等相關(guān)知識(shí)。
          10. 重構(gòu)?。≈灰阌X得這個(gè)地方有問(wèn)題了,那就不要等到以后。以后往往就是再也不。

          結(jié)束

          說(shuō)到這可能會(huì)有一種戛然而止的感覺。在這一篇文章里面,我們首先以兩個(gè)優(yōu)化代碼的具體實(shí)例為引子,讓大家明白了一些業(yè)務(wù)代碼的優(yōu)化思路。在之后從列舉了一些其他可能出現(xiàn)的錯(cuò)誤,以及是優(yōu)化代碼的思想準(zhǔn)備和理論指導(dǎo)。

          其實(shí)都是希望大家能夠在業(yè)務(wù)中去發(fā)現(xiàn)問(wèn)題,再去思考如何解決問(wèn)題,因?yàn)檎f(shuō)了那么多,到底能不把代碼寫好。還是得靠自己。

          干貨分享

          最近將個(gè)人學(xué)習(xí)筆記整理成冊(cè),使用PDF分享。關(guān)注我,回復(fù)如下代碼,即可獲得百度盤地址,無(wú)套路領(lǐng)?。?/p>

          ?001:《Java并發(fā)與高并發(fā)解決方案》學(xué)習(xí)筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學(xué)習(xí)筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)速成)》?007:全部?008:加技術(shù)群討論

          加個(gè)關(guān)注不迷路

          喜歡就點(diǎn)個(gè)"在看"唄^_^

          瀏覽 25
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品无码在线播放 | 波多野结衣无码精品一区 | 国产熟妇XXXXXⅩ性Ⅹ交 | 国产精品自拍偷拍 | 久久久久成人片免费观看蜜芽 |