17611538698
webmaster@21cto.com

请停止在 Java 中使用 if-else 语句

编程语言 0 603 2024-09-05 06:27:02

图片

导读:


反 IF 编程方法由 Francesco Cirillo 于 2007 年发起,它改变了许多人对软件设计中的 IF 和条件句的看法。


软件设计的核心是一种简单但潜在危险的工具:条件“IF”语句。不可否认,它的基础性在于,它用于处理增长环境中的变化,被称为“IF 策略”,它可能是一个无声的破坏者,使代码复杂化和逻辑混乱。


IF 策略可能导致调试问题、从未交付的用户故事、技术债务、纠结的设计、团队中的摩擦和其他低效率。


从本质上讲,IF 策略会增加成本并延迟软件交付时间,同时降低内部质量。

有一个传说中古老的遗留系统,我的同事从上到下编码了数千行,每个块都添加了几个 if then java 部分来做路由,我感觉自己被困在一个陌生的仙境中。

在这篇文章中,我将向大家展示如何优化 if-else 的使用,限制具有太多分支条件的代码,并使我们的 Java 代码更易于阅读和理解。

代码片段包含太多 if-else 语句


在深入研究优化细节之前,让我们使用一个包含多个 if else 条件的示例演示 Java 代码,然后以各种方式对其进行优化:


public class ShippingCostCalculator {    public double calculateShippingCost(String shippingType, double weight) {        if (shippingType.equals("STANDARD")) {            return weight * 5.0;        } else if (shippingType.equals("EXPRESS")) {            return weight * 10.0;        } else if (shippingType.equals("SAME_DAY")) {            return weight * 20.0;        } else if (shippingType.equals("INTERNATIONAL")) {            return weight * 50.0;        } else if (shippingType.equals("OVERNIGHT")) {            return weight * 30.0;        }        return 0;    }}


如各位所见,上面的代码功能是根据快递类型计算运费。

使用枚举进行优化


现在,我们将使用Enum来替换语句if-else:


public enum ShippingType {    STANDARD {        @Override        public double getCost(double weight) {            return weight * 5.0;        }    },    EXPRESS {        @Override        public double getCost(double weight) {            return weight * 10.0;        }    },    SAME_DAY {        @Override        public double getCost(double weight) {            return weight * 20.0;        }    },    INTERNATIONAL {        @Override        public double getCost(double weight) {            return weight * 50.0;        }    },    OVERNIGHT {        @Override        public double getCost(double weight) {            return weight * 30.0;        }    };
public abstract double getCost(double weight);}


public class ShippingCostCalculator {
public double calculateShippingCost(ShippingType shippingType, double weight) { return shippingType.getCost(weight); }}


public class MainCost {    public static void main(String[] args) {        var calculator = new ShippingCostCalculator();        var cost = calculator.calculateShippingCost(ShippingType.EXPRESS, 2.5);        System.out.println("Shipping cost: " + cost);    }}


可以看到,复杂的 if-else 语句被简化成了两行简短、直接的代码。运行 main 函数,查看结果。

图片


优势:

  • 可扩展性:添加新的运输类型和值。枚举并定义处理方法。

  • 可维护且可理解的代码:每种传输方法的原因都是独立的且易于理解。


然而,使用Enum也有一些明显的缺点,您应该考虑:

  • 可扩展性:添加新的运输类型和值。枚举并定义处理方法。

  • 难以添加新参数:当需要更多参数时,Enum 不太适合,并且代码变得繁琐。

  • 继承限制:Enum不能从其他类继承,这降低了它们重用逻辑的潜力。


使用 Enum 进行优化通常适用于参数较少的简单情况。

使用工厂模式进行优化


还是上面乱七八糟的代码,我们按照如下方式进行优化:

创建接口 ShippingCostStrategy

public interface ShippingCostStrategy {    double calculate(double weight);}

现在我们将创建一个工厂类来处理基于运输类型的策略路由:

import java.util.HashMap;import java.util.Map;
public class ShippingCostFactory { private static final Map strategies = new HashMap<>();
static { strategies.put("STANDARD", new StandardShipping()); strategies.put("EXPRESS", new ExpressShipping()); strategies.put("SAME_DAY", new SameDayShipping()); strategies.put("INTERNATIONAL", new InternationalShipping()); strategies.put("OVERNIGHT", new OvernightShipping()); }
public static ShippingCostStrategy getStrategy(String shippingType) { ShippingCostStrategy strategy = strategies.get(shippingType); if (strategy == null) { throw new IllegalArgumentException("Invalid shipping type: " + shippingType); } return strategy; }}

现在只需调用并使用它。

public class ShippingCostCalculator {    public double calculateShippingCost(String shippingType, double weight) {        ShippingCostStrategy strategy = ShippingCostFactory.getStrategy(shippingType);        return strategy.calculate(weight);    }}


工厂模式的优点:

  • 易于扩展:只需开发附加类并更新工厂即可添加新的交付类型,而无需更改核心代码。

  • 逻辑分离:收费逻辑是独立的,易于管理和维护。

  • 灵活性:工厂可能会根据其他因素返回多个解决方案,从而提高灵活性。


使用策略模式进行优化


在我们详细讨论之前,请记住,实现方式与工厂类似,但使用目的略有不同:

public interface ShippingCostStrategy {    double calculate(double weight);}


public class StandardShipping implements ShippingCostStrategy {    @Override    public double calculate(double weight) {        return weight * 5.0;    }}
public class ExpressShipping implements ShippingCostStrategy { @Override public double calculate(double weight) { return weight * 10.0; }}
public class SameDayShipping implements ShippingCostStrategy { @Override public double calculate(double weight) { return weight * 20.0; }}
public class InternationalShipping implements ShippingCostStrategy { @Override public double calculate(double weight) { return weight * 50.0; }}
public class OvernightShipping implements ShippingCostStrategy { @Override public double calculate(double weight) { return weight * 30.0; }}

现在我们将创建 ShippingContext 来管理策略。

public class ShippingCostContext {    private ShippingCostStrategy strategy;
public void setStrategy(ShippingCostStrategy strategy) { this.strategy = strategy; }
public double calculateShippingCost(double weight) { return strategy.calculate(weight); }}
import java.util.HashMap;import java.util.Map;
public class ShippingCostCalculator {
private static final Map strategies = new HashMap<>();
static { strategies.put("STANDARD", new StandardShipping()); strategies.put("EXPRESS", new ExpressShipping()); strategies.put("SAME_DAY", new SameDayShipping()); strategies.put("INTERNATIONAL", new InternationalShipping()); strategies.put("OVERNIGHT", new OvernightShipping()); }
private final ShippingCostContext context = new ShippingCostContext();
public double calculateShippingCost(String shippingType, double weight) { ShippingCostStrategy strategy = strategies.get(shippingType); if (strategy == null) { throw new IllegalArgumentException("Invalid shipping type: " + shippingType); } context.setStrategy(strategy); return context.calculateShippingCost(weight); }}


现在,把它放进主类里面使用。

public class MainCost {
public static void main(String[] args) ShippingCostCalculator calculator = new ShippingCostCalculator();
double weight = 10.0;
String shippingType1 = "STANDARD"; double cost1 = calculator.calculateShippingCost(shippingType1, weight); System.out.println("Shipping cost for " + shippingType1 + ": " + cost1);
String shippingType2 = "EXPRESS"; double cost2 = calculator.calculateShippingCost(shippingType2, weight); System.out.println("Shipping cost for " + shippingType2 + ": " + cost2);
String shippingType3 = "SAME_DAY"; double cost3 = calculator.calculateShippingCost(shippingType3, weight); System.out.println("Shipping cost for " + shippingType3 + ": " + cost3);
String shippingType4 = "INTERNATIONAL"; double cost4 = calculator.calculateShippingCost(shippingType4, weight); System.out.println("Shipping cost for " + shippingType4 + ": " + cost4);
String shippingType5 = "OVERNIGHT"; double cost5 = calculator.calculateShippingCost(shippingType5, weight); System.out.println("Shipping cost for " + shippingType5 + ": " + cost5); }
}

图片


在前两种情况下,策略模式管理如何计算运输成本,而工厂模式根据运输类型决定采用哪种策略。

使用 Stream API 和 Map 进行优化


import java.util.HashMap;import java.util.Map;
public class ShippingCostCalculator {
private static final Map shippingCosts = new HashMap<>();
static { shippingCosts.put("STANDARD", 5.0); shippingCosts.put("EXPRESS", 10.0); shippingCosts.put("SAME_DAY", 20.0); shippingCosts.put("INTERNATIONAL", 50.0); shippingCosts.put("OVERNIGHT", 30.0); }
public double calculateShippingCost(String shippingType, double weight) { return shippingCosts.entrySet().stream() .filter(entry -> entry.getKey().equalsIgnoreCase(shippingType)) .map(Map.Entry::getValue) .findFirst() .orElse(0.0) * weight; }
public static void main(String[] args) { ShippingCostCalculator calculator = new ShippingCostCalculator();
double weight = 10.0;
String shippingType1 = "STANDARD"; double cost1 = calculator.calculateShippingCost(shippingType1, weight); System.out.println("Shipping cost for " + shippingType1 + ": " + cost1);
String shippingType2 = "EXPRESS"; double cost2 = calculator.calculateShippingCost(shippingType2, weight); System.out.println("Shipping cost for " + shippingType2 + ": " + cost2);
String shippingType3 = "SAME_DAY"; double cost3 = calculator.calculateShippingCost(shippingType3, weight); System.out.println("Shipping cost for " + shippingType3 + ": " + cost3);
String shippingType4 = "INTERNATIONAL"; double cost4 = calculator.calculateShippingCost(shippingType4, weight); System.out.println("Shipping cost for " + shippingType4 + ": " + cost4);
String shippingType5 = "OVERNIGHT"; double cost5 = calculator.calculateShippingCost(shippingType5, weight); System.out.println("Shipping cost for " + shippingType5 + ": " + cost5);
String invalidType = "INVALID"; double invalidCost = calculator.calculateShippingCost(invalidType, weight); System.out.println("Shipping cost for " + invalidType + ": " + invalidCost); }}


图片

这个策略也比较方便,虽然扩展性不如Factory和Strategy,但对于简单的场景来说还是一个可行的选择。

谢谢大家,在您离开之前:

👏 请为文章点赞或转发 👉
也欢迎在下面的评论部分分享您的问题或见解。

本文最初于 2024 年 8 月 20 日发布于https://cafeincode.com

作者:陈兴

评论