Java如何避免過多的if else、switch case

前言:
最近在開發的過程中,發現代碼不優化的話,會出現很多的if else,改成switch case也避免不了代碼豎向擴展,感覺蠻冗餘的,我的場景是這樣的,一個電商平臺在淘寶、京東、拼多多、蘇寧、小紅書等渠道都有店鋪,不同的渠道需要對訂單進行不同的處理,如果你有類似這樣的場景,不妨可以參考一下本文,如果文章有誤或者有更好的解決方案,還望指出。

本文涉及的代碼在github上,點擊 鏈接 可查看源碼。

文章會給出兩種解決方案,第一種是結合簡單工廠模式和策略模式,第二種是利用Java8內置的函數式接口,精簡了代碼,我在開發中也是用這個方案來簡化代碼。

我們先來看這樣的一個場景:根據快遞包裹的重量計算快遞公司的郵費,不同的快遞公司有不同的郵費計算方式,如京東,申通,圓通等快遞公司的郵費計算都是不一樣的。

  • 快遞公司枚舉類如下:
public enum ParcelCompanyEnum {

    ZTO("中通快遞"),YTO("圓通快遞"),STO("申通快遞"),JD("京東快遞");

    String name;

    ParcelCompanyEnum(String name) {
        this.name = name;
    }
}
  • if else的方式,可以看到代碼有些冗餘:
    public Double calculatePostage1(ParcelCompanyEnum company, Integer weight) {
        if (company == ParcelCompanyEnum.JD) {
            return 10 + weight * 1.2;
        } else if (company == ParcelCompanyEnum.STO) {
            return 12 + weight * 0.8;
        } else if (company == ParcelCompanyEnum.YTO) {
            return 8 + weight * 1.5;
        } else if (company == ParcelCompanyEnum.ZTO) {
            return 9 + weight * 1.1;
        } else {
            throw new IllegalArgumentException("No such company :" + company);
        }
    }
  • 換成switch case 精簡了不少代碼,如果快遞公司很多的話,看上去還是蠻冗餘的:
    public Double calculatePostage2(ParcelCompanyEnum company, Integer weight) {
        switch (company) {
            case JD: return 10 + weight * 1.2;
            case STO: return 12 + weight * 0.8;
            case YTO: return 8 + weight * 1.5;
            case ZTO: return 9 + weight * 1.1;
            default: throw new IllegalArgumentException("No such company :"+company);
        }
    }
一、結合簡單工廠模式和策略模式優化代碼

簡單工廠模式(Simple Factory Pattern):有一個工廠類,通過傳遞不同的參數,返回不同所需的對象,簡而言之是造對象。

策略模式(Strategy Pattern):將不同的策略(所實現的功能)封裝起來,客戶端在運行時選擇某種策略(解決方案)來執行。

我們需要對不同快遞公司計算不同的郵費當成是策略,在需要計算郵費的時候根據快遞公司來選擇不同的策略來計算郵費。

  • 計算郵費策略接口:
public interface CalculateStrategy {
    Double calculate(Integer weight);
}
  • 京東快遞郵費計算,實現策略接口:
public class CalculateStrategyJd implements CalculateStrategy {
    @Override
    public Double calculate(Integer weight) {
        return 10 + weight * 1.2;
    }
}
  • 申通快遞郵費計算,實現策略接口:
public class CalculateStrategySto implements CalculateStrategy {
    @Override
    public Double calculate(Integer weight) {
        return 12 + weight * 0.8;
    }
}
  • 圓通快遞郵費計算,實現策略接口:
public class CalculateStrategyYto implements CalculateStrategy {
    @Override
    public Double calculate(Integer weight) {
        return 8 + weight * 1.5;
    }
}
  • 中通快遞郵費計算,實現策略接口:
public class CalculateStrategyZto implements CalculateStrategy {
    @Override
    public Double calculate(Integer weight) {
        return 9 + weight * 1.1;
    }
}

我們還需一個工廠類,負責創建實現策略接口的類,其實這裏的Map起了很大的作用,也是這裏避免過多的if else、switch case邏輯,運用簡單工廠模式和策略模式實際上也做了解耦,因爲在實際開發中對不同分支邏輯的處理可不是這麼簡單的一條公式,工廠類如下:

public class CalculateFactory {
    private Map<ParcelCompanyEnum, CalculateStrategy> map = new HashMap<>(5);

    {
        map.put(ParcelCompanyEnum.JD, new CalculateStrategyJd());
        map.put(ParcelCompanyEnum.YTO, new CalculateStrategyYto());
        map.put(ParcelCompanyEnum.ZTO, new CalculateStrategyZto());
        map.put(ParcelCompanyEnum.STO, new CalculateStrategySto());
    }
    public CalculateStrategy creat(ParcelCompanyEnum company) {
        return map.get(company);
    }
}

有人可能會想,能不能Map的value直接是計算郵費的方法呢?實際上是有的,待會且看方案二,用Java8內置函數式接口來完成。
接着,我們只需要定義一個這樣的方法,就可以用簡單工廠模式和策略模式來完成計算郵費了:

    public Double calculatePostage3(ParcelCompanyEnum company, Integer weight) {
        CalculateFactory factory = new CalculateFactory();
        return factory.creat(company).calculate(weight);
    }
二、利用Java8內置函數式接口優化代碼

計算郵費的類,這裏就不給出全部代碼了,只給出運用Java8內置接口的相關邏輯,如果要看全代碼的話,文章開頭處有鏈接,這裏用的是Java8的內置接口Function<T,R>,待實現的方法邏輯是參數類型傳入泛型T類,返回類型是泛型R類,可以看我們的例子,Map的value是Function<Integer, Double>,我們需要實現apply方法,方法參數類型是Integer,即重量,方法返回類型是Double,即郵費,代碼如下:

public class CalculatePostage {

     Map<ParcelCompanyEnum, Function<Integer, Double>> map = new HashMap<>(5);

    {
        map.put(ParcelCompanyEnum.JD, this::calculateJd);
        map.put(ParcelCompanyEnum.STO, this::calculatSto);
        map.put(ParcelCompanyEnum.YTO, this::calculateYto);
        map.put(ParcelCompanyEnum.ZTO, this::calculateZto);
    }
    public Double calculateJd(Integer weight) {
        return 10 + weight * 1.2;
    }
    public Double calculatSto(Integer weight) {
        return 12 + weight * 0.8;
    }
    public Double calculateYto(Integer weight) {
        return 8 + weight * 1.5;
    }
    public Double calculateZto(Integer weight) {
        return 9 + weight * 1.1;
    }
}

綜合方案一和方案二,客戶端代碼如下:

public class Client {

    public static void main(String[] args) {
        ParcelCompanyEnum company = ParcelCompanyEnum.JD;
        Integer weight = 15;
        CalculatePostage calculatePostage = new CalculatePostage();

        System.out.println("if else 計算郵費:" + calculatePostage.calculatePostage1(company, weight));

        System.out.println("switch case 計算郵費:" + calculatePostage.calculatePostage2(company, weight));

        System.out.println("傳統工廠模式 + 策略模式 計算郵費:" + calculatePostage.calculatePostage3(company, weight));

        System.out.println("Java8 lambda + 策略模式 計算郵費:" + calculatePostage.map.get(company).apply(weight));
    }
}

Java8內置的函數式接口:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
三、拓展Java8提供的內置函數式接口
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章