导读:
反 IF 编程方法由 Francesco Cirillo 于 2007 年发起,它改变了许多人对软件设计中的 IF 和条件句的看法。
软件设计的核心是一种简单但潜在危险的工具:条件“IF”语句。不可否认,它的基础性在于,它用于处理增长环境中的变化,被称为“IF 策略”,它可能是一个无声的破坏者,使代码复杂化和逻辑混乱。
IF 策略可能导致调试问题、从未交付的用户故事、技术债务、纠结的设计、团队中的摩擦和其他低效率。
从本质上讲,IF 策略会增加成本并延迟软件交付时间,同时降低内部质量。
有一个传说中古老的遗留系统,我的同事从上到下编码了数千行,每个块都添加了几个 if then java 部分来做路由,我感觉自己被困在一个陌生的仙境中。
在这篇文章中,我将向大家展示如何优化 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;
}
}
如各位所见,上面的代码功能是根据快递类型计算运费。
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);
}
}
在前两种情况下,策略模式管理如何计算运输成本,而工厂模式根据运输类型决定采用哪种策略。
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。
作者:陈兴
本文为 @ 场长 创作并授权 21CTO 发布,未经许可,请勿转载。
内容授权事宜请您联系 webmaster@21cto.com或关注 21CTO 公众号。
该文观点仅代表作者本人,21CTO 平台仅提供信息存储空间服务。