主頁 >  其他 > 爛慫if-else代碼優化方案

爛慫if-else代碼優化方案

2023-06-01 08:19:55 其他

0.問題概述

代碼可讀性是衡量代碼質量的重要標準,可讀性也是可維護性、可擴展性的保證,因為代碼是連接程式員和機器的中間橋梁,要對雙邊友好,Quora 上有一個帖子: “What are some of the most basic things every programmer should know?”

其中:

  • Code that’s hard to understand is hard to maintain.

  • Code that’s hard to maintain is next to useless.

也強調了"easy understand"代碼的重要性,

寫這篇文章的貧訓是在研讀Apache ShenYu專案時,看到了很大一坨的if else陳述句,如下:
image.png

這里并非評論這段代碼寫法有問題,因為我還并沒有深入到專案細節之中,可能這已經是多輪優化的結果嘞,

但是這個多層if else的形式引發了我的思考,因為我也曾在專案代碼中引入過如此繁重的if else結構,并在Code Review中被指出了問題,從那以后,我對if else的最大容忍層數就是三層,

我把大量if else的場景按照深度和廣度兩個維度劃分為兩種情況:

  • 嵌套層級過深

  • 平鋪范圍太廣

下面就討論一下,當代碼中存在大量這樣結構的代碼的時候,該如何優化?

1.解決方案

1.1 盡早回傳

又稱衛陳述句,即Guard Statement

WikiPedia:

In computer programming, aguardis abooleanexpressionthat must evaluate to true if the program execution is to continue in the branch in question.

Regardless of which programming language is used, aguard clause,guard code, orguard statement, is a check of integritypreconditionsused to avoid errors during execution. A typical example is checking that a reference about to be processed is not null, which avoids null-pointer failures. Other uses include using a boolean field foridempotence(so subsequent calls are nops), as in thedispose pattern. The guard provides anearly exitfrom asubroutine, and is a commonly used deviation fromstructured programming, removing one level of nesting and resulting in flatter code:[1]replacingif guard { ... }withif not guard: return; ....

實際應用:

if (CollectionUtils.isNotEmpty(list)) {
	// do something
} else {   
	return xxx;
}


使用盡早回傳優化:

if (CollectionUtils.isEmpty(list)) {
	return xxx;
}

// do something


可以看到,優化后的代碼不僅節省了一個else陳述句,也能讓后續的"do something"節省一層if else包裹,代碼看起來更干凈一些

結合這個例子再說一下我對衛陳述句的理解:

可以將“衛”理解為“門衛”,門衛的作用是檢查過濾,只有符合條件的陳述句,才可以繼續執行,否則直接勸返(return),吐槽一下這種中文直譯有些晦澀,未免有點“德先生賽先生”的意思了,,,

1.2 使用switch或三元運算子

可以利用語法知識,對if else進行簡化,

例如,當if else滿足一定條件時:

if (condition1) {
    doSomeThing1();
} else if (condition2) {
    doSomeThing2();
} else if (condition3) {
    doSomeThing3(); 
} else if (condition4) {
    doSomeThing4();
} else {
    doSomeThing5(); 
}...


可以使用switch case語法進行替換

或,

例如使用三元運算子進行賦值操作:

Integer num = obejct == null ? 1 : object.value();

1.3 策略模式

1.3.1 概念

策略模式是一種行為設計模式,即一個物件有一個確定的行為,在不同場景下,這些行為有不同的演算法實作,

例如從內蒙通過公共交通去北京是一個確定的行為,在天上這種場景可以選擇飛機,地上的場景可以選擇火車~

策略模式一般包含三個要素:

  • 抽象策略(Abstract strategy):定義所謂的“確定的行為”,一般由介面或抽象類實作

  • 具體實作(Concrete strategy):封裝對應場景下的具體演算法實作,

  • 背景關系(Context):負責具體實作策略的管理并供物件使用,

1.3.2 使用場景

  • 一個介面或抽象類的各個子類都是為了解決相同的問題,區分這些子類的只有方法實作的不同,

  • 代碼中使用大量if else或大面積switch case來選擇具體的子實作類

1.3.3 實際應用

例如:

if ("man".equals(strategy)) {   
	// Perform related operations 
} else if ("woman".equals(strategy)) {   
	// Perform operations related to women
} else if ("other".equals(strategy)) {   
	// Perform other operations
}


上面一段代碼,每一個if分支完成的都是相同的操作,只是在不同的性別場景下,操作方法的實作不同,那么就可以使用策略模式進行優化:

首先,定義一個抽象策略介面:

public interface Strategy {

    void run() throws Exception;

}


然后,進行不同策略的實作:

//Men's strategy implementation class
@Slf4j
public class ManStrategy implements Strategy {

    @Override
    public void run() throws Exception {
        // Fast man's logic
        log.debug("Execute the logic related to men...");
    }

}

//Women's strategy implementation class
@Slf4j
public class WomanStrategy implements Strategy {

    @Override
    public void run() throws Exception {
        // Fast woman's logic
        log.debug("Execute women related logic...");
    }

}

//Others' policy implementation class
@Slf4j
public class OtherStrategy implements Strategy {

    @Override
    public void run() throws Exception {
        // Fast other logic
        log.debug("Perform other related logic...");
    }

}


最后,進行策略的應用:

public class StrategyTest {

    public static void main(String[] args) {
        try {
            Strategy strategy = initMap("man");
            strategy.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //Initialize the Map to obtain a gender policy
    private static Strategy initMap(String key) {
        //Use simple example
        HashMap<String, Strategy> map = new HashMap<>();
        map.put("man", new ManStrategy());
        map.put("woman", new WomanStrategy());
        map.put("other", new OtherStrategy());
        return map.get(key);
    }

}


1.3.4 優劣勢分析及優化

1.3.4.1 劣勢

整體上來看,使用策略模式雖然剔除了大量的if else陳述句,但是也引入了更多的類檔案,同時在Context中需要維護一個類似注冊表的map物件,當增加策略實作時,容易忘記,

優化措施:

在Java中,可以使用函式式編程進行優化:

@Slf4j
public class StrategyTest {

    public static void main(String[] args) {
        //Use simple example
        HashMap<String, Strategy> map = new HashMap<>();
        map.put("man", () -> log.debug("Execute the logic related to men..."));
        map.put("woman", () -> log.debug("Execute women related logic..."));
        map.put("other", () -> log.debug("Execute logic related to others..."));

        try {
            map.get("woman").run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


或者,使用列舉進行優化:

@Slf4j
public enum Strategy {

    //Man state
    MAN(0) {
        @Override
        void run() {
            //Perform related operations
            log.debug("Execute the logic related to men");
        }
    },
    //Woman state
    WOMAN(1) {
        @Override
        void run() {
            //Perform operations related to women
            log.debug("Execute women related logic");
        }
    },
    //Other status
    OTHER(2) {
        @Override
        void run() {
            //Perform other related operations
            log.debug("Perform other related logic");
        }
    };

    abstract void run();

    public int statusCode;

    Strategy(int statusCode) {
        this.statusCode = statusCode;
    }

}


public static void main(String[] args) {
        try {
            //Simple use example
            String param = String.valueOf(Strategy.WOMAN);
            Strategy strategy = Strategy.valueOf(param);
            strategy.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
}


除此以外,在客戶端實際使用策略時,即物件進行方法的呼叫時,客戶端必須知道這個策略的所有實作子類,并需要了解這些子類之間的不同以及各自的應用場景,這樣客戶端才能選擇合適的策略實作“確定的行為”,

1.3.4.2 優勢

  • 最直接的好處就是可以讓又臭又長的if else代碼塊看起來更干凈,

  • 面向物件的三大特點:封裝、繼承、多型,在策略模式中都能找到影子,面向介面編程,代碼的可擴展性好

  • 代碼的可測性好,Mock更方便,減少了分支判斷,實作類只需要各自測驗即可,

1.4 Optional

if else分支判斷的很多情況都是進行非空條件的判斷,Optional是Java8開始提供的新特性,使用這個語法特性,也可以減少代碼中if else的數量,例如:

優化前:

String str = "Hello World!";

if (str != null) {
    System.out.println(str);
} else {
    System.out.println("Null");
}


優化后:

Optional<String> optional = Optional.of("Hello World!");
optional.ifPresentOrElse(System.out::println, () -> System.out.println("Null"));


1.5 注冊表

這種方式和策略模式有相似之處,但注冊表更自由,不需要提煉介面,只需要將自定義實作在注冊表中注冊即可,

例如,優化前:

if (param.equals(value1)) {
    doAction1(someParams);
}else if (param.equals(value2)) {
    doAction2(someParams);
}else if (param.equals(value3)) {
    doAction3(someParams);
}


優化后:

//Generic here? For the convenience of demonstration, it can be replaced with the real type we need in actual development
Map<?, Function<?> action> actionMappings = new HashMap<>(); 
// When init
actionMappings.put(value1, (someParams) -> { doAction1(someParams)});
actionMappings.put(value2, (someParams) -> { doAction2(someParams)});
actionMappings.put(value3, (someParams) -> { doAction3(someParams)});
 
// Omit null judgment
actionMappings.get(param).apply(someParams);


1.6 責任鏈模式

先來看一段代碼:

public void handle(request) {
    if (handlerA.canHandle(request)) {
        handlerA.handleRequest(request);
    } else if (handlerB.canHandle(request)) {
        handlerB.handleRequest(request);
    } else if (handlerC.canHandle(request)) {
        handlerC.handleRequest(request);
    }
}


代碼中也是存在一坨if else陳述句,但是和上述例子不同之處在于,if條件判斷權在每個handler組件中,每一個handler的判斷方式也可能不盡相同,相當靈活,同一個request可能同時滿足多個if條件

解決方案就是參考開源組件中Filter或者Interceptor責任鏈機制,優化后代碼:

public void handle(request) {
  handlerA.handleRequest(request);
}
 
public abstract class Handler {
    
  protected Handler next;
    
  public abstract void handleRequest(Request request);
    
  public void setNext(Handler next) { this.next = next; }
}
 
public class HandlerA extends Handler {
  public void handleRequest(Request request) {
    if (canHandle(request)) doHandle(request);
    else if (next != null) next.handleRequest(request);
  }
}


2.總結&思考

這篇文章主要介紹了代碼中if else代碼塊泛濫時的治理措施,在實際應用時可根據具體場景選擇合理的方案,

其實代碼中存在大面積if else本無問題,用一句網路流行語來反駁就是:“你就說能不能用吧!”,但是作為有追求的工程師,我們要對專案以及代碼負責,要及時的識別到代碼中的壞味道,并持續重構優化,最后還想說一定要擁抱開源,多研讀他人優秀代碼,并臨摹、思考、實踐,日拱一卒,不期而至,

3.參考

  • https://programmer.ink/think/how-to-optimize-if-there-are-too-many-if-statements-in-java-code-of-series-17.html
  • WikiPedia
  • Quora

作者:京東零售 韓超

來源:京東云開發者社區

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/553969.html

標籤:其他

上一篇:解讀與用戶一起“跳動”的開源實時監控工具 HertzBeat

下一篇:返回列表

標籤雲
其他(160094) Python(38193) JavaScript(25469) Java(18172) C(15235) 區塊鏈(8268) C#(7972) AI(7469) 爪哇(7425) MySQL(7219) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5344) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4580) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1979) 功能(1967) Web開發(1951) HtmlCss(1950) C++(1928) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 爛慫if-else代碼優化方案

    # 0.問題概述 代碼可讀性是衡量代碼質量的重要標準,可讀性也是可維護性、可擴展性的保證,因為代碼是連接程式員和機器的中間橋梁,要對雙邊友好。Quora 上有一個帖子: “What are some of the most basic things every programmer should k ......

    uj5u.com 2023-06-01 08:19:55 more
  • 解讀與用戶一起“跳動”的開源實時監控工具 HertzBeat

    摘要:開源專案遇上華為云,會擦出怎樣的火花? 在本期《開源實時監控工具HertzBeat如何與用戶一起“跳動? 》的主題直播中,HertzBeat & TanCloud 創始人鞏超與開發者和伙伴朋友們交流當前主流指標監控方案,解讀HertzBeat及能力特點,并為大家演示了如何通過華為云商店安裝部署 ......

    uj5u.com 2023-06-01 08:19:21 more
  • 讀書的竅門

    提供一個讀書老竅門 - "讀萬卷書". 特別是對于讀國內書籍, 比較好用, 自己之前 準備出書和編輯們聊過, 聊了幾次后暫停出書計劃, 當時編輯說了一個頗有智慧的話: "有深度的書讓國外出就行了". 但凡想找點意思并挖掘全貌的話, 可能類似書籍要讀 個幾十本, 然后可能其中一兩本的作者比較實在零星說 ......

    uj5u.com 2023-06-01 08:18:24 more
  • 2023年4月和5月隨筆

    之后可能也采用交替發文,只是不同的系列交替發,這一方面是為了照顧不同的讀者,另一方面則是希望能多一些時間緩沖一下,多一點時間潤色一下。 ......

    uj5u.com 2023-06-01 08:17:57 more
  • React Native+小程式容器=更高的開發效率

    React Native是由Facebook開發并于2015年首次發布的一個框架,用于構建原始的移動應用程式。 它具有許多技術上的優勢: 跨平臺開發:使用React Native,您可以使用相同的代碼庫構建同時運行在iOS和Android平臺上的應用程式。這種跨平臺的開發方式可以大大減少開發作業量和 ......

    uj5u.com 2023-06-01 08:17:52 more
  • 5.5. Java并發工具類(如CountDownLatch、CyclicBarrier等)

    #### 5.5.1 CountDownLatch `CountDownLatch`是一個同步輔助類,它允許一個或多個執行緒等待,直到其他執行緒完成一組操作。`CountDownLatch`有一個計數器,當計數器減為0時,等待的執行緒將被喚醒。計數器只能減少,不能增加。 **示例:使用CountDownL ......

    uj5u.com 2023-06-01 08:12:24 more
  • 4.4. 物件序列化與反序列化

    在本節中,我們將詳細討論Java中的物件序列化與反序列化概念、使用方法以及實體。物件序列化是將物件的狀態資訊轉換為位元組流的程序,而反序列化則相反,是將位元組流恢復為物件的程序。 #### 4.4.1 為什么需要物件序列化? 物件序列化的主要目的是為了在不同的系統間傳輸物件,或者將物件持久化到磁盤檔案中 ......

    uj5u.com 2023-06-01 08:00:50 more
  • 百度飛槳(PaddlePaddle) - PP-OCRv3 文字檢測識別系統 基于 Padd

    Paddle Serving 是飛槳服務化部署框架,能夠幫助開發者輕松實作從移動端、服務器端呼叫深度學習模型的遠程預測服務。 Paddle Serving圍繞常見的工業級深度學習模型部署場景進行設計,具備完整的在線服務能力,支持的功能包括多模型管理、模型熱加載、基于Baidu-RPC的高并發低延遲響... ......

    uj5u.com 2023-05-31 10:34:11 more
  • 3.5. 例外處理

    在Java中,例外是一種用于表示程式在運行程序中遇到的錯誤或例外情況的物件。Java提供了一套例外處理機制,可以幫助我們更好地處理運行時可能出現的錯誤和例外。例外處理的主要概念包括: 1. 例外類:Java中的例外類是繼承自`Throwable`類的類。例外類分為兩大類:`Error`類和`Exce ......

    uj5u.com 2023-05-31 10:28:49 more
  • 3.5. 例外處理

    在Java中,例外是一種用于表示程式在運行程序中遇到的錯誤或例外情況的物件。Java提供了一套例外處理機制,可以幫助我們更好地處理運行時可能出現的錯誤和例外。例外處理的主要概念包括: 1. 例外類:Java中的例外類是繼承自`Throwable`類的類。例外類分為兩大類:`Error`類和`Exce ......

    uj5u.com 2023-05-31 10:11:20 more