<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>

          這滿屏的 if/ else,交接的兄弟快被逼瘋!

          共 3237字,需瀏覽 7分鐘

           ·

          2020-12-27 22:27


          點(diǎn)擊上方?泥瓦匠 關(guān)注我!

          老家浙江東海邊,靠海吃海,目前經(jīng)營一個(gè)小品牌,讓普通人吃到最新鮮的海鮮。有興趣可以點(diǎn)擊了解:《浙里有漁,鮮人一步!》???


          為什么我們寫的代碼都是 if-else?

          程序員想必都經(jīng)歷過這樣的場(chǎng)景:剛開始自己寫的代碼很簡(jiǎn)潔,邏輯清晰,函數(shù)精簡(jiǎn),沒有一個(gè) if-else,可隨著代碼邏輯不斷完善和業(yè)務(wù)的瞬息萬變:比如需要對(duì)入?yún)⑦M(jìn)行類型和值進(jìn)行判斷;這里要判斷下對(duì)象是否為 null;不同類型執(zhí)行不同的流程。

          落地到具體實(shí)現(xiàn)只能不停地加 if-else 來處理,漸漸地,代碼變得越來越龐大,函數(shù)越來越長,文件行數(shù)也迅速突破上千行,維護(hù)難度也越來越大,到后期基本達(dá)到一種難以維護(hù)的狀態(tài)。

          雖然我們都很不情愿寫出滿屏 if-else 的代碼,可邏輯上就是需要特殊判斷,很絕望,可也沒辦法避免啊。

          其實(shí)回頭看看自己的代碼,寫 if-else 不外乎兩種場(chǎng)景:異常邏輯處理和不同狀態(tài)處理。

          兩者最主要的區(qū)別是:異常邏輯處理說明只能一個(gè)分支是正常流程,而不同狀態(tài)處理都所有分支都是正常流程。

          怎么理解?舉個(gè)例子:

          ?1//舉例一:異常邏輯處理例子
          ?2Object?obj?=?getObj();
          ?3if?(obj?!=?null)?{
          ?4????//do?something
          ?5}else{
          ?6????//do?something
          ?7}
          ?8
          ?9//舉例二:狀態(tài)處理例子
          10Object?obj?=?getObj();
          11if?(obj.getType?==?1)?{
          12????//do?something
          13}else?if?(obj.getType?==?2)?{
          14????//do?something
          15}else{
          16????//do?something
          17}

          第一個(gè)例子 if (obj != null) 是異常處理,是代碼健壯性判斷,只有 if 里面才是正常的處理流程,else 分支是出錯(cuò)處理流程;而第二個(gè)例子不管 type 等于 1,2 還是其他情況,都屬于業(yè)務(wù)的正常流程。對(duì)于這兩種情況重構(gòu)的方法也不一樣。

          代碼 if-else 代碼太多有什么缺點(diǎn)?

          缺點(diǎn)相當(dāng)明顯了:最大的問題是代碼邏輯復(fù)雜,維護(hù)性差,極容易引發(fā) bug。如果使用 if-else,說明 if 分支和 else 分支的重視是同等的,但大多數(shù)情況并非如此,容易引起誤解和理解困難。

          是否有好的方法優(yōu)化?如何重構(gòu)?

          方法肯定是有的。重構(gòu) if-else 時(shí),心中無時(shí)無刻把握一個(gè)原則:

          盡可能地維持正常流程代碼在最外層。

          意思是說,可以寫 if-else 語句時(shí)一定要盡量保持主干代碼是正常流程,避免嵌套過深。

          實(shí)現(xiàn)的手段有:減少嵌套、移除臨時(shí)變量、條件取反判斷、合并條件表達(dá)式等。關(guān)注公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師可以獲取一份全套的 Java 架構(gòu)視頻。

          下面舉幾個(gè)實(shí)例來講解這些重構(gòu)方法:

          異常邏輯處理型重構(gòu)方法實(shí)例一

          重構(gòu)前:

          ?1double?disablityAmount(){
          ?2????if(_seniority?2)
          ?3????????return?0;
          ?4
          ?5????if(_monthsDisabled?>?12)
          ?6????????return?0;
          ?7
          ?8????if(_isPartTime)
          ?9????????return?0;
          10
          11????//do?somethig
          12}

          重構(gòu)后:

          1double?disablityAmount(){
          2????if(_seniority?2?||?_monthsDisabled?>?12?||?_isPartTime)
          3????????return?0;
          4
          5????//do?somethig
          6}
          這里的重構(gòu)手法叫合并條件表達(dá)式:如果有一系列條件測(cè)試都得到相同結(jié)果,將這些結(jié)果測(cè)試合并為一個(gè)條件表達(dá)式。

          這個(gè)重構(gòu)手法簡(jiǎn)單易懂,帶來的效果也非常明顯,能有效地較少if語句,減少代碼量邏輯上也更加易懂。

          異常邏輯處理型重構(gòu)方法實(shí)例二

          重構(gòu)前:

          ?1double?getPayAmount(){
          ?2????double?result;
          ?3????if(_isDead)?{
          ?4????????result?=?deadAmount();
          ?5????}else{
          ?6????????if(_isSeparated){
          ?7????????????result?=?separatedAmount();
          ?8????????}
          ?9????????else{
          10????????????if(_isRetired){
          11????????????????result?=?retiredAmount();
          12????????????else{
          13????????????????result?=?normalPayAmount();
          14????????????}
          15????????}
          16????}
          17????return?result;
          18}

          重構(gòu)后:

          ?1double?getPayAmount(){
          ?2????if(_isDead)
          ?3????????return?deadAmount();
          ?4
          ?5????if(_isSeparated)
          ?6????????return?separatedAmount();
          ?7
          ?8????if(_isRetired)
          ?9????????return?retiredAmount();
          10
          11????return?normalPayAmount();
          12}

          怎么樣?比對(duì)兩個(gè)版本,會(huì)發(fā)現(xiàn)重構(gòu)后的版本邏輯清晰,簡(jiǎn)潔易懂。

          和重構(gòu)前到底有什么區(qū)別呢?

          最大的區(qū)別是減少 if-else 嵌套。可以看到,最初的版本 if-else 最深的嵌套有三層,看上去邏輯分支非常多,進(jìn)到里面基本都要被繞暈。其實(shí),仔細(xì)想想嵌套內(nèi)的 if-else 和最外層并沒有關(guān)聯(lián)性的,完全可以提取最頂層。

          改為平行關(guān)系,而非包含關(guān)系,if-else 數(shù)量沒有變化,但是邏輯清晰明了,一目了然。

          另一個(gè)重構(gòu)點(diǎn)是廢除了 result 臨時(shí)變量,直接 return 返回。好處也顯而易見直接結(jié)束流程,縮短異常分支流程。原來的做法先賦值給 result 最后統(tǒng)一 return,那么對(duì)于最后 return 的值到底是那個(gè)函數(shù)返回的結(jié)果不明確,增加了一層理解難度。

          總結(jié)重構(gòu)的要點(diǎn):如果 if-else 嵌套沒有關(guān)聯(lián)性,直接提取到第一層,一定要避免邏輯嵌套太深。盡量減少臨時(shí)變量改用 return 直接返回。

          異常邏輯處理型重構(gòu)方法實(shí)例三

          重構(gòu)前:

          1public?double?getAdjustedCapital(){
          2????double?result?=?0.0;
          3????if(_capital?>?0.0?){
          4????????if(_intRate?>?0?&&?_duration?>0){
          5????????????resutl?=?(_income?/?_duration)?*ADJ_FACTOR;
          6????????}
          7????}
          8????return?result;
          9}

          第一步,運(yùn)用第一招,減少嵌套和移除臨時(shí)變量:

          1public?double?getAdjustedCapital(){
          2????if(_capital?<=?0.0?){
          3????????return?0.0;
          4????}
          5????if(_intRate?>?0?&&?_duration?>0){
          6????????return?(_income?/?_duration)?*ADJ_FACTOR;
          7????}
          8????return?0.0;
          9}

          這樣重構(gòu)后,還不夠,因?yàn)橹饕恼Z句 (_income / _duration) *ADJ_FACTOR; 在 if 內(nèi)部,并非在最外層,根據(jù)優(yōu)化原則(盡可能地維持正常流程代碼在最外層),可以再繼續(xù)重構(gòu):

          ?1public?double?getAdjustedCapital(){
          ?2????if(_capital?<=?0.0?){
          ?3????????return?0.0;
          ?4????}
          ?5????if(_intRate?<=?0?||?_duration?<=?0){
          ?6????????return?0.0;
          ?7????}
          ?8
          ?9????return?(_income?/?_duration)?*ADJ_FACTOR;
          10}

          這才是好的代碼風(fēng)格,邏輯清晰,一目了然,沒有 if-else 嵌套難以理解的流程。

          這里用到的重構(gòu)方法是:將條件反轉(zhuǎn)使異常情況先退出,讓正常流程維持在主干流程。

          異常邏輯處理型重構(gòu)方法實(shí)例四

          重構(gòu)前:

          ?1???/*?查找年齡大于18歲且為男性的學(xué)生列表?*/
          ?2????public?ArrayList?getStudents(int?uid){
          ?3????????ArrayList?result?=?new?ArrayList();
          ?4????????Student?stu?=?getStudentByUid(uid);
          ?5????????if?(stu?!=?null)?{
          ?6????????????Teacher?teacher?=?stu.getTeacher();
          ?7????????????if(teacher?!=?null){
          ?8????????????????ArrayList?students?=?teacher.getStudents();
          ?9????????????????if(students?!=?null){
          10????????????????????for(Student?student?:?students){
          11????????????????????????if(student.getAge()?>?=?18?&&?student.getGender()?==?MALE){
          12????????????????????????????result.add(student);
          13????????????????????????}
          14????????????????????}
          15????????????????}else?{
          16????????????????????logger.error("獲取學(xué)生列表失敗");
          17????????????????}
          18????????????}else?{
          19????????????????logger.error("獲取老師信息失敗");
          20????????????}
          21????????}?else?{
          22????????????logger.error("獲取學(xué)生信息失敗");
          23????????}
          24????????return?result;
          25????}

          典型的"箭頭型"代碼,最大的問題是嵌套過深,解決方法是異常條件先退出,保持主干流程是核心流程:

          重構(gòu)后:

          ?1???/*?查找年齡大于18歲且為男性的學(xué)生列表?*/
          ?2????public?ArrayList?getStudents(int?uid){
          ?3????????ArrayList?result?=?new?ArrayList();
          ?4????????Student?stu?=?getStudentByUid(uid);
          ?5????????if?(stu?==?null)?{
          ?6????????????logger.error("獲取學(xué)生信息失敗");
          ?7????????????return?result;
          ?8????????}
          ?9
          10????????Teacher?teacher?=?stu.getTeacher();
          11????????if(teacher?==?null){
          12????????????logger.error("獲取老師信息失敗");
          13????????????return?result;
          14????????}
          15
          16????????ArrayList?students?=?teacher.getStudents();
          17????????if(students?==?null){
          18????????????logger.error("獲取學(xué)生列表失敗");
          19????????????return?result;
          20????????}
          21
          22????????for(Student?student?:?students){
          23????????????if(student.getAge()?>?18?&&?student.getGender()?==?MALE){
          24????????????????result.add(student);
          25????????????}
          26????????}
          27????????return?result;
          28????}

          狀態(tài)處理型重構(gòu)方法實(shí)例一

          重構(gòu)前:

          ?1double?getPayAmount(){
          ?2????Object?obj?=?getObj();
          ?3????double?money?=?0;
          ?4????if?(obj.getType?==?1)?{
          ?5????????ObjectA?objA?=?obj.getObjectA();
          ?6????????money?=?objA.getMoney()*obj.getNormalMoneryA();
          ?7????}
          ?8????else?if?(obj.getType?==?2)?{
          ?9????????ObjectB?objB?=?obj.getObjectB();
          10????????money?=?objB.getMoney()*obj.getNormalMoneryB()+1000;
          11????}
          12}

          重構(gòu)后:

          ?1double?getPayAmount(){
          ?2????Object?obj?=?getObj();
          ?3????if?(obj.getType?==?1)?{
          ?4????????return?getType1Money(obj);
          ?5????}
          ?6????else?if?(obj.getType?==?2)?{
          ?7????????return?getType2Money(obj);
          ?8????}
          ?9}
          10
          11double?getType1Money(Object?obj){
          12????ObjectA?objA?=?obj.getObjectA();
          13????return?objA.getMoney()*obj.getNormalMoneryA();
          14}
          15
          16double?getType2Money(Object?obj){
          17????ObjectB?objB?=?obj.getObjectB();
          18????return?objB.getMoney()*obj.getNormalMoneryB()+1000;
          19}

          這里使用的重構(gòu)方法是:把 if-else 內(nèi)的代碼都封裝成一個(gè)公共函數(shù)。函數(shù)的好處是屏蔽內(nèi)部實(shí)現(xiàn),縮短 if-else 分支的代碼。代碼結(jié)構(gòu)和邏輯上清晰,能一下看出來每一個(gè)條件內(nèi)做的功能。

          狀態(tài)處理型重構(gòu)方法實(shí)例二

          針對(duì)狀態(tài)處理的代碼,一種優(yōu)雅的做法是用多態(tài)取代條件表達(dá)式(《重構(gòu)》推薦做法)。


          你手上有個(gè)條件表達(dá)式,它根據(jù)對(duì)象類型的不同而選擇不同的行為。將這個(gè)表達(dá)式的每個(gè)分支放進(jìn)一個(gè)子類內(nèi)的覆寫函數(shù)中,然后將原始函數(shù)聲明為抽象函數(shù)。

          重構(gòu)前:

          ?1double?getSpeed(){
          ?2????switch(_type){
          ?3????????case?EUROPEAN:
          ?4????????????return?getBaseSpeed();
          ?5????????case?AFRICAN:
          ?6????????????return?getBaseSpeed()-getLoadFactor()*_numberOfCoconuts;
          ?7????????case?NORWEGIAN_BLUE:
          ?8????????????return?(_isNailed)?0:getBaseSpeed(_voltage);
          ?9????}
          10}

          重構(gòu)后:

          ?1class?Bird{
          ?2????abstract?double?getSpeed();
          ?3}
          ?4
          ?5class?European?extends?Bird{
          ?6????double?getSpeed(){
          ?7????????return?getBaseSpeed();
          ?8????}
          ?9}
          10
          11class?African?extends?Bird{
          12????double?getSpeed(){
          13????????return?getBaseSpeed()-getLoadFactor()*_numberOfCoconuts;
          14????}
          15}
          16
          17class?NorwegianBlue?extends?Bird{
          18????double?getSpeed(){
          19????????return?(_isNailed)?0:getBaseSpeed(_voltage);
          20????}
          21}

          可以看到,使用多態(tài)后直接沒有了 if-else,但使用多態(tài)對(duì)原來代碼修改過大,需要一番功夫才行。最好在設(shè)計(jì)之初就使用多態(tài)方式。關(guān)注公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師可以獲取架構(gòu)視頻。

          總結(jié)

          if-else 代碼是每一個(gè)程序員最容易寫出的代碼,同時(shí)也是最容易被寫爛的代碼,稍不注意,就產(chǎn)生一堆難以維護(hù)和邏輯混亂的代碼。

          針對(duì)條件型代碼重構(gòu)把握一個(gè)原則:

          盡可能地維持正常流程代碼在最外層,保持主干流程是正常核心流程。

          為維持這個(gè)原則:合并條件表達(dá)式可以有效地減少if語句數(shù)目;減少嵌套能減少深層次邏輯;異常條件先退出自然而然主干流程就是正常流程。

          針對(duì)狀態(tài)處理型重構(gòu)方法有兩種:一種是把不同狀態(tài)的操作封裝成函數(shù),簡(jiǎn)短 if-else 內(nèi)代碼行數(shù);另一種是利用面向?qū)ο蠖鄳B(tài)特性直接干掉了條件判斷。

          現(xiàn)在回頭看看自己的代碼,犯了哪些典型錯(cuò)誤,趕緊運(yùn)用這些重構(gòu)方法重構(gòu)代碼吧??!

          老家浙江東海邊,靠海吃海,目前經(jīng)營一個(gè)小品牌,讓普通人吃到最新鮮的海鮮。有興趣可以點(diǎn)擊了解:《浙里有漁,鮮人一步!》???

          往期推薦

          消息隊(duì)列(MQ)詳細(xì)總結(jié),程序員必看!

          如何手動(dòng)獲取 Spring 容器中的 Bean?

          開發(fā)中,那些常用的 MySQL 優(yōu)化

          實(shí)現(xiàn)用戶一鍵登錄的方案有哪些?

          點(diǎn)擊領(lǐng)?。撼绦騿T最新學(xué)習(xí)資料!

          下方二維碼關(guān)注我

          技術(shù)草根,堅(jiān)持分享?編程,算法,架構(gòu)

          朋友助力下!點(diǎn)個(gè)在看!
          瀏覽 57
          點(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动漫 | 欧美天堂在线 | 国产激情自拍 | 免费av毛片网站 免费av中文字幕 |