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提供的内置函数式接口
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章