Java if-else 多重嵌套的優(yōu)化方式
if-else多重嵌套的優(yōu)化
1. if-else 多重嵌套的問題
項目重構(gòu)發(fā)現(xiàn)代碼中存在類似以下的三重 if-else 嵌套代碼,其中變量 a、b、c有三種可能的取值,組合起來共有27 個分支,這還沒有算上對各個變量進行合法性校驗失敗的分支。
如此繁雜瑣碎的代碼閱讀起來有多費勁可想而知,而條件判斷太多對理解業(yè)務(wù)邏輯也是個問題,更不用提之后的擴展維護
if (a == 1) { if (b == 1) { if (c == 1) { } else if (c == 2) { } else if (c == 3) { } else { } } else if (b == 2) { } ...... } else if (a == 2) { ...... }
2. 解決方案
條件判斷嵌套過多的害處毋庸多言,仔細思索后大致整理了優(yōu)化思路,首要點是將多重條件判斷進行整合。如以上例子中對 3 個變量的取值逐一判斷來確定一條分支其實可以優(yōu)化為以 3 個變量聚合為一個條件進行分支確定,這樣分支數(shù)并不會減少,但是對條件的判斷必然是更為清晰的
2.1 使用Map緩存
基于以上例子,變量 a、b、c都是整數(shù)類型的數(shù)據(jù),則可以考慮將其合并為一個String 數(shù)據(jù),以字符串"111" 來表示 a=1, b=1, c=1 的條件,這樣可以用一個 Map 結(jié)構(gòu)來存儲對應(yīng)操作標識,從而實現(xiàn)策略切換。這種方式通常會另外新建一個工具類,通過工具類來獲取對應(yīng)條件的結(jié)果
public static Map<String, MatchVerifyResultDTO> getInstance() { if (upCreditMap.isEmpty()) { MatchVerifyResultDTO invalidResult = new MatchVerifyResultDTO(false, MatchVerifyEnum.MVC_UP_CREDIT_ERR.getCode()); MatchVerifyResultDTO validResult = new MatchVerifyResultDTO(true, MatchVerifyEnum.MVC_PASS.getCode()); upCreditMap.put("111", invalidResult); upCreditMap.put("112", validResult); upCreditMap.put("113", validResult); ...... } return upCreditMap; }
2.2 switch 簡化條件
如果不準備新建工具類來實現(xiàn)條件簡化,也可以采用 switch結(jié)構(gòu)。不過不管何種方式,核心思想是不變的,即采用整體數(shù)據(jù)來消弭小的差異判斷,此處依然將變量 a、b、c合并為一個String 特征條件
switch(attribute) { case "111": case "112": handleTargetMessage(); break; ...... default: log.error("Not supported attribute!") break; }
多個ifelse語句的替代設(shè)計
ifelse是任何編程語言的重要組成部分。但是我們編寫了大量嵌套的if語句,這使得我們的代碼更加復(fù)雜和難以維護。
接下來,讓我們探索如何簡化代碼的中的ifelse語句寫法。
案例研究
我們經(jīng)常遇到涉及很多條件的業(yè)務(wù)邏輯,并且每個邏輯都需要不同的處理方式。以Calculator類為例。我們將有一個方法,它接受兩個數(shù)字和一個運算符作為輸入,并根據(jù)操作返回結(jié)果:
public int calculate(int a, int b, String operator) { ? ? int result = Integer.MIN_VALUE;? ? ? if ("add".equals(operator)) { ? ? ? ? result = a + b; ? ? } else if ("multiply".equals(operator)) { ? ? ? ? result = a * b; ? ? } else if ("divide".equals(operator)) { ? ? ? ? result = a / b; ? ? } else if ("subtract".equals(operator)) { ? ? ? ? result = a - b; ? ? } ? ? return result; }
我們也可以使用switch語句來實現(xiàn)它:
public int calculateUsingSwitch(int a, int b, String operator) { ? ? switch (operator) { ? ? case "add": ? ? ? ? result = a + b; ? ? ? ? break; ? ? // other cases ? ? ? ? } ? ? return result; }
在典型的開發(fā)中,if語句可能會變得更大,更復(fù)雜。此外,當(dāng)存在復(fù)雜條件時,switch語句不適合。
擁有嵌套決策結(jié)構(gòu)的另一個副作用是它們變得難以管理。例如,如果我們需要添加一個新的運算符,我們必須添加一個新的if語句并實現(xiàn)該操作。
重構(gòu)
可以通過設(shè)計模式,來達到我們要的效果。
工廠模式
很多時候,我們遇到ifelse結(jié)構(gòu),最終在每個分支中執(zhí)行類似的操作。這提供了提取工廠方法的機會,該工廠方法返回給定類型的對象并基于具體對象行為執(zhí)行操作。
對于我們的示例,讓我們定義一個具有單個apply方法的Operation接口:
public interface Operation { ? ? int apply(int a, int b); }
該方法將兩個數(shù)字作為輸入并返回結(jié)果。讓我們定義一個用于執(zhí)行添加的類:
public class Addition implements Operation { ? ? @Override ? ? public int apply(int a, int b) { ? ? ? ? return a + b; ? ? } }
我們現(xiàn)在將實現(xiàn)一個工廠類,它根據(jù)給定的運算符返回Operation的實例:
public class OperatorFactory { ? ? static Map<String, Operation> operationMap = new HashMap<>(); ? ? static { ? ? ? ? operationMap.put("add", new Addition()); ? ? ? ? operationMap.put("divide", new Division()); ? ? ? ? // more operators ? ? }? ? ? public static Optional<Operation> getOperation(String operator) { ? ? ? ? return Optional.ofNullable(operationMap.get(operator)); ? ? } }
現(xiàn)在,在Calculator類中,我們可以查詢工廠以獲取相關(guān)操作并應(yīng)用源數(shù):
public int calculateUsingFactory(int a, int b, String operator) { ? ? Operation targetOperation = OperatorFactory ? ? ? .getOperation(operator) ? ? ? .orElseThrow(() -> new IllegalArgumentException("Invalid Operator")); ? ? return targetOperation.apply(a, b); }
在這個例子中,我們已經(jīng)看到了如何將責(zé)任委托給工廠類提供的松散耦合對象。但是有可能嵌套的if語句只是轉(zhuǎn)移到了工廠類,這違背了我們的目的。
或者,我們可以在Map中維護一個對象存儲庫,可以查詢該存儲庫以進行快速查找。正如我們所見,OperatorFactory#operationMap服務(wù)于我們的目的。我們還可以在運行時初始化Map并將它們配置為查找。
使用枚舉
除了使用Map之外,我們還可以使用Enum來標記特定的業(yè)務(wù)邏輯。之后,我們可以在嵌套的if語句或switch case 語句中使用它們?;蛘?,我們也可以將它們用作對象的工廠并制定策略以執(zhí)行相關(guān)的業(yè)務(wù)邏輯。
這樣可以減少嵌套if語句的數(shù)量,并將責(zé)任委托給單個Enum值。
讓我們看看我們?nèi)绾螌崿F(xiàn)它。首先,我們需要定義我們的枚舉:
public enum Operator { ? ? ADD, MULTIPLY, SUBTRACT, DIVIDE }
可以觀察到,這些值是不同運算符的標簽,將進一步用于計算。我們總是可以選擇在嵌套的if語句或switch case中使用這些值作為不同的條件,但讓我們設(shè)計一種將邏輯委托給Enum本身的替代方法。
我們將為每個Enum值定義方法并進行計算。例如:
ADD { ? ? @Override ? ? public int apply(int a, int b) { ? ? ? ? return a + b; ? ? } }, // other operators? public abstract int apply(int a, int b);
然后在Calculator類中,我們可以定義一個執(zhí)行操作的方法:
public int calculate(int a, int b, Operator operator) { return operator.apply(a, b); }
現(xiàn)在,我們可以通過使用Operator#valueOf()方法將String值轉(zhuǎn)換為Operator來調(diào)用該方法:
@Test public void test() { ? ? Calculator calculator = new Calculator(); ? ? int result = calculator.calculate(3, 4, Operator.valueOf("ADD")); ? ? assertEquals(7, result); }
命令模式
在前面的討論中,我們已經(jīng)看到使用工廠類來返回給定運算符的正確業(yè)務(wù)對象的實例。稍后,業(yè)務(wù)對象用于在計算器中執(zhí)行計算。
我們還可以設(shè)計一個Calculator#calculate方法來接受可以在輸入上執(zhí)行的命令。這將是替換嵌套if語句的另一種方法。
我們首先定義我們的Command接口:
public interface Command { ? ? Integer execute(); }
接下來,讓我們實現(xiàn)一個AddCommand:
public class AddCommand implements Command { ? ? // Instance variables ? ? ? public AddCommand(int a, int b) { ? ? ? ? this.a = a; ? ? ? ? this.b = b; ? ? }? ? ? @Override ? ? public Integer execute() { ? ? ? ? return a + b; ? ? } }
最后,讓我們在Calculator中引入一個接受并執(zhí)行Command的新方法:
public int calculate(Command command) { ? ? return command.execute(); }
接下來,我們可以通過實例化AddCommand調(diào)用計算并將其發(fā)送到Calculator#calculate方法:
@Test public void test() { ? ? Calculator calculator = new Calculator(); ? ? int result = calculator.calculate(new AddCommand(3, 7)); ? ? assertEquals(10, result); }
規(guī)則引擎
當(dāng)我們最終編寫大量嵌套if語句時,每個條件都描述了一個業(yè)務(wù)規(guī)則,必須對其進行評估才能處理正確的邏輯。規(guī)則引擎從主代碼中獲取了這種復(fù)雜性。一個RuleEngine評估規(guī)則和返回基于輸入的結(jié)果。
讓我們通過設(shè)計一個簡單的RuleEngine來演示一個例子,該RuleEngine通過一組規(guī)則處理Expression并返回所選規(guī)則的結(jié)果。首先,我們將定義一個Rule接口:
public interface Rule { ? ? boolean evaluate(Expression expression); ? ? Result getResult(); }
其次,讓我們實現(xiàn)一個RuleEngine:
public class RuleEngine { ? ? private static List<Rule> rules = new ArrayList<>();? ? ? static { ? ? ? ? rules.add(new AddRule()); ? ? }? ? ? public Result process(Expression expression) { ? ? ? ? Rule rule = rules ? ? ? ? ? .stream() ? ? ? ? ? .filter(r -> r.evaluate(expression)) ? ? ? ? ? .findFirst() ? ? ? ? ? .orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule")); ? ? ? ? return rule.getResult(); ? ? } }
所述RuleEngine接受一個表達對象,并返回結(jié)果?,F(xiàn)在,讓我們將Expression類設(shè)計為一組包含兩個Integer對象的Operator,它將被應(yīng)用:
public class Expression { ? ? private Integer x; ? ? private Integer y; ? ? private Operator operator; ? ? ? ? }
最后讓我們定義一個自定義的AddRule類,該類僅在指定ADD操作時進行求值:
public class AddRule implements Rule { ? ? @Override ? ? public boolean evaluate(Expression expression) { ? ? ? ? boolean evalResult = false; ? ? ? ? if (expression.getOperator() == Operator.ADD) { ? ? ? ? ? ? this.result = expression.getX() + expression.getY(); ? ? ? ? ? ? evalResult = true; ? ? ? ? } ? ? ? ? return evalResult; ? ? } ? ? }
我們現(xiàn)在將使用Expression調(diào)用RuleEngine:
@Test public void test() { ? ? Expression expression = new Expression(5, 5, Operator.ADD); ? ? RuleEngine engine = new RuleEngine(); ? ? Result result = engine.process(expression);? ? ? assertNotNull(result); ? ? assertEquals(10, result.getValue()); }
小結(jié)
通過這些設(shè)計模式,可以作為我們的ifelse語句的替代方案,具體用哪一種可以根據(jù)你的實際業(yè)務(wù)場景來決定。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
IntelliJ IDEA中如何構(gòu)建Spring Boot的項目
這篇文章主要介紹了IntelliJ IDEA中如何構(gòu)建Spring Boot的項目問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Jpa?Specification如何實現(xiàn)and和or同時使用查詢
這篇文章主要介紹了Jpa?Specification如何實現(xiàn)and和or同時使用查詢,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11SpringBoot集成 Prometheus進行高效監(jiān)控的實現(xiàn)
Prometheus作為一個開源的監(jiān)控和告警工具,以其強大的數(shù)據(jù)采集、存儲和查詢能力,受到了眾多開發(fā)者的青睞,本文主要介紹了SpringBoot集成 Prometheus進行高效監(jiān)控的實現(xiàn),感興趣的可以了解一下2024-07-07Spring實戰(zhàn)之FileSystemResource加載資源文件示例
這篇文章主要介紹了Spring實戰(zhàn)之FileSystemResource加載資源文件,結(jié)合實例形式分析了spring FileSystemResource加載xml資源文件的具體實現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2019-12-12