Java中多個(gè)ifelse語(yǔ)句的替代設(shè)計(jì)

概述

ifelse是任何編程語(yǔ)言的重要組成部分衷咽。但是我們編寫(xiě)了大量嵌套的if語(yǔ)句,這使得我們的代碼更加復(fù)雜和難以維護(hù)走净。

接下來(lái)银择,讓我們探索如何簡(jiǎn)化代碼的中的ifelse語(yǔ)句寫(xiě)法。

案例研究

我們經(jīng)常遇到涉及很多條件的業(yè)務(wù)邏輯懦尝,并且每個(gè)邏輯都需要不同的處理方式知纷。以Calculator類為例。我們將有一個(gè)方法陵霉,它接受兩個(gè)數(shù)字和一個(gè)運(yùn)算符作為輸入琅轧,并根據(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語(yǔ)句來(lái)實(shí)現(xiàn)它:

public int calculateUsingSwitch(int a, int b, String operator) {
    switch (operator) {
    case "add":
        result = a + b;
        break;
    // other cases    
    }
    return result;
}

在典型的開(kāi)發(fā)中,if語(yǔ)句可能會(huì)變得更大踊挠,更復(fù)雜乍桂。此外,當(dāng)存在復(fù)雜條件時(shí)效床,switch語(yǔ)句不適合睹酌。

擁有嵌套決策結(jié)構(gòu)的另一個(gè)副作用是它們變得難以管理。例如剩檀,如果我們需要添加一個(gè)新的運(yùn)算符憋沿,我們必須添加一個(gè)新的if語(yǔ)句并實(shí)現(xiàn)該操作。

重構(gòu)

可以通過(guò)設(shè)計(jì)模式沪猴,來(lái)達(dá)到我們要的效果辐啄。

工廠模式

很多時(shí)候,我們遇到ifelse結(jié)構(gòu)运嗜,最終在每個(gè)分支中執(zhí)行類似的操作壶辜。這提供了提取工廠方法的機(jī)會(huì),該工廠方法返回給定類型的對(duì)象并基于具體對(duì)象行為執(zhí)行操作担租。

對(duì)于我們的示例砸民,讓我們定義一個(gè)具有單個(gè)apply方法的Operation接口:

public interface Operation {
    int apply(int a, int b);
}

該方法將兩個(gè)數(shù)字作為輸入并返回結(jié)果。讓我們定義一個(gè)用于執(zhí)行添加的類:

public class Addition implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}

我們現(xiàn)在將實(shí)現(xiàn)一個(gè)工廠類翩活,它根據(jù)給定的運(yùn)算符返回Operation的實(shí)例:

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);
}

在這個(gè)例子中菠镇,我們已經(jīng)看到了如何將責(zé)任委托給工廠類提供的松散耦合對(duì)象冗荸。但是有可能嵌套的if語(yǔ)句只是轉(zhuǎn)移到了工廠類,這違背了我們的目的利耍。

或者蚌本,我們可以在Map中維護(hù)一個(gè)對(duì)象存儲(chǔ)庫(kù)盔粹,可以查詢?cè)摯鎯?chǔ)庫(kù)以進(jìn)行快速查找。正如我們所見(jiàn)程癌,OperatorFactory#operationMap服務(wù)于我們的目的舷嗡。我們還可以在運(yùn)行時(shí)初始化Map并將它們配置為查找。

使用枚舉

除了使用Map之外嵌莉,我們還可以使用Enum來(lái)標(biāo)記特定的業(yè)務(wù)邏輯进萄。之后,我們可以在嵌套的if語(yǔ)句或switch case 語(yǔ)句中使用它們锐峭≈惺螅或者,我們也可以將它們用作對(duì)象的工廠并制定策略以執(zhí)行相關(guān)的業(yè)務(wù)邏輯沿癞。

這樣可以減少嵌套if語(yǔ)句的數(shù)量援雇,并將責(zé)任委托給單個(gè)Enum值。

讓我們看看我們?nèi)绾螌?shí)現(xiàn)它椎扬。首先惫搏,我們需要定義我們的枚舉:

public enum Operator {
    ADD, MULTIPLY, SUBTRACT, DIVIDE
}

可以觀察到,這些值是不同運(yùn)算符的標(biāo)簽蚕涤,將進(jìn)一步用于計(jì)算筐赔。我們總是可以選擇在嵌套的if語(yǔ)句或switch case中使用這些值作為不同的條件,但讓我們?cè)O(shè)計(jì)一種將邏輯委托給Enum本身的替代方法揖铜。

我們將為每個(gè)Enum值定義方法并進(jìn)行計(jì)算川陆。例如:

ADD {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
},
// other operators
 
public abstract int apply(int a, int b);

然后在Calculator類中,我們可以定義一個(gè)執(zhí)行操作的方法:

public int calculate(int a, int b, Operator operator) {
return operator.apply(a, b);
}

現(xiàn)在蛮位,我們可以通過(guò)使用Operator#valueOf()方法將String值轉(zhuǎn)換為Operator來(lái)調(diào)用該方法:

@Test
public void test() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
    assertEquals(7, result);
}

命令模式

在前面的討論中,我們已經(jīng)看到使用工廠類來(lái)返回給定運(yùn)算符的正確業(yè)務(wù)對(duì)象的實(shí)例鳞绕。稍后失仁,業(yè)務(wù)對(duì)象用于在計(jì)算器中執(zhí)行計(jì)算。

我們還可以設(shè)計(jì)一個(gè)Calculator#calculate方法來(lái)接受可以在輸入上執(zhí)行的命令们何。這將是替換嵌套if語(yǔ)句的另一種方法萄焦。

我們首先定義我們的Command接口:

public interface Command {
    Integer execute();
}

接下來(lái),讓我們實(shí)現(xiàn)一個(gè)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;
    }
}

最后冤竹,讓我們?cè)贑alculator中引入一個(gè)接受并執(zhí)行Command的新方法:

public int calculate(Command command) {
    return command.execute();
}

接下來(lái)拂封,我們可以通過(guò)實(shí)例化AddCommand調(diào)用計(jì)算并將其發(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)我們最終編寫(xiě)大量嵌套if語(yǔ)句時(shí),每個(gè)條件都描述了一個(gè)業(yè)務(wù)規(guī)則鹦蠕,必須對(duì)其進(jìn)行評(píng)估才能處理正確的邏輯冒签。規(guī)則引擎從主代碼中獲取了這種復(fù)雜性。一個(gè)RuleEngine評(píng)估規(guī)則和返回基于輸入的結(jié)果钟病。

讓我們通過(guò)設(shè)計(jì)一個(gè)簡(jiǎn)單的RuleEngine來(lái)演示一個(gè)例子萧恕,該RuleEngine通過(guò)一組規(guī)則處理Expression并返回所選規(guī)則的結(jié)果刚梭。首先,我們將定義一個(gè)Rule接口:

public interface Rule {
    boolean evaluate(Expression expression);
    Result getResult();
}

其次票唆,讓我們實(shí)現(xiàn)一個(gè)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接受一個(gè)表達(dá)對(duì)象朴读,并返回結(jié)果。現(xiàn)在走趋,讓我們將Expression類設(shè)計(jì)為一組包含兩個(gè)Integer對(duì)象的Operator衅金,它將被應(yīng)用:

public class Expression {
    private Integer x;
    private Integer y;
    private Operator operator;        
}

最后讓我們定義一個(gè)自定義的AddRule類,該類僅在指定ADD操作時(shí)進(jìn)行求值:

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é)論

通過(guò)這些設(shè)計(jì)模式簿煌,可以作為我們的ifelse語(yǔ)句的替代方案氮唯,具體用哪一種可以根據(jù)你的實(shí)際業(yè)務(wù)場(chǎng)景來(lái)決定。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啦吧,一起剝皮案震驚了整個(gè)濱河市您觉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌授滓,老刑警劉巖琳水,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異般堆,居然都是意外死亡在孝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)淮摔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)私沮,“玉大人,你說(shuō)我怎么就攤上這事和橙∽醒啵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵魔招,是天一觀的道長(zhǎng)晰搀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)办斑,這世上最難降的妖魔是什么外恕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮乡翅,結(jié)果婚禮上鳞疲,老公的妹妹穿的比我還像新娘。我一直安慰自己蠕蚜,他們只是感情好尚洽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著靶累,像睡著了一般翎朱。 火紅的嫁衣襯著肌膚如雪橄维。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天拴曲,我揣著相機(jī)與錄音争舞,去河邊找鬼。 笑死澈灼,一個(gè)胖子當(dāng)著我的面吹牛竞川,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叁熔,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼委乌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了荣回?” 一聲冷哼從身側(cè)響起遭贸,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎心软,沒(méi)想到半個(gè)月后壕吹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡删铃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年耳贬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猎唁。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咒劲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诫隅,到底是詐尸還是另有隱情腐魂,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布逐纬,位于F島的核電站挤渔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏风题。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一嫉父、第九天 我趴在偏房一處隱蔽的房頂上張望沛硅。 院中可真熱鬧,春花似錦绕辖、人聲如沸摇肌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)围小。三九已至昵骤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肯适,已是汗流浹背变秦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留框舔,地道東北人蹦玫。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像刘绣,于是被迫代替她去往敵國(guó)和親樱溉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • if else每種編程語(yǔ)言都不可或缺的條件語(yǔ)句纬凤,在編程時(shí)會(huì)大量的用到福贞。一般建議嵌套不要超過(guò)三層,如果一段代碼存在過(guò)...
    Djbfifjd閱讀 3,538評(píng)論 3 38
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,810評(píng)論 0 38
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5停士? 答:HTML5是最新的HTML標(biāo)準(zhǔn)挖帘。 注意:講述HT...
    kismetajun閱讀 27,486評(píng)論 1 45
  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL向瓷、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥(niǎo)慢飛閱讀 5,523評(píng)論 0 4
  • C++運(yùn)算符重載-下篇 本章內(nèi)容:1. 運(yùn)算符重載的概述2. 重載算術(shù)運(yùn)算符3. 重載按位運(yùn)算符和二元邏輯運(yùn)算符4...
    Haley_2013閱讀 1,443評(píng)論 0 49