[解锁新姿势] 兄dei,你代码需要优化了
在之前文章说到,简单 if-else
,可以使用 卫语句
进行优化。但是在实际开发中,往往不是简单 if-else
结构,我们通常会 不经意间
写下如下代码:
-------------------- 理想中的 if-else -------------------- public void today() { if (isWeekend()) { System.out.println("玩游戏"); } else { System.out.println("上班!"); } } -------------------- 现实中的 if-else -------------------- if (money >= 1000) { if (type == UserType.SILVER_VIP.getCode()) { System.out.println("白银会员 优惠50元"); result = money - 50; } else if (type == UserType.GOLD_VIP.getCode()) { System.out.println("黄金会员 8折"); result = money * 0.8; } else if (type == UserType.PLATINUM_VIP.getCode()) { System.out.println("白金会员 优惠50元,再打7折"); result = (money - 50) * 0.7; } else { System.out.println("普通会员 不打折"); result = money; } } //省略 n 个 if-else ...... 复制代码
毫不夸张的说,我们都写过类似的代码,回想起被 if-else
支配的恐惧,我们常常无所下手,甚至不了了之。
下面分享一下我在开发中遇到复杂的 if-else
语句 “优雅处理”
思路。如有不妥,欢迎大家一起交流学习。
假设有这么一个需求:
一个电商系统,当用户消费 满1000
金额,可以根据用户VIP等级,享受打折优惠。
根据用户VIP等级,计算出用户最终的费用。
private static double getResult(long money, int type) { double result = money; if (money >= 1000) { if (type == UserType.SILVER_VIP.getCode()) { System.out.println("白银会员 优惠50元"); result = money - 50; } else if (type == UserType.GOLD_VIP.getCode()) { System.out.println("黄金会员 8折"); result = money * 0.8; } else if (type == UserType.PLATINUM_VIP.getCode()) { System.out.println("白金会员 优惠50元,再打7折"); result = (money - 50) * 0.7; } else { System.out.println("普通会员 不打折"); result = money; } } return result; } 复制代码
为了方便演示,代码上我进行了简单实现,但实际上 if - else
会进行 复杂的逻辑
计费。
从功能上来说,基本完成,但是对于我这种有代码洁癖的人来说,代码质量上不忍直视。我们开始着手 优化
一下我们的 第一版代码
吧。
看到如上代码,聪明的朋友首先想到的是,这不是典型的 策略模式
吗?
你可真是个机灵鬼,我们先尝试用策略模式来优化一下代码吧。
可能有的朋友还不清楚,什么是策略模式。策略模式是定义一系列的算法,把它们一个个 封装
起来, 并且使它们可相互 替换
。
比如上述需求,有 返利
、有 打折
、有 折上折
等等。这些算法本身就是一种 策略
。并且这些算法可以相互 替换
的,比如今天我想让 白银会员优惠50
,明天可以替换为 白银会员打9折
。
说了那么多,不如编码来得实在。
public interface Strategy { // 计费方法 double compute(long money); } // 普通会员策略 public class OrdinaryStrategy implements Strategy { @Override public double compute(long money) { System.out.println("普通会员 不打折"); return money; } } // 白银会员策略 public class SilverStrategy implements Strategy { @Override public double compute(long money) { System.out.println("白银会员 优惠50元"); return money - 50; } } // 黄金会员策略 public class GoldStrategy implements Strategy{ @Override public double compute(long money) { System.out.println("黄金会员 8折"); return money * 0.8; } } // 白金会员策略 public class PlatinumStrategy implements Strategy { @Override public double compute(long money) { System.out.println("白金会员 优惠50元,再打7折"); return (money - 50) * 0.7; } } 复制代码
我们定义来一个 Strategy
接口,并且定义 四个子类,实现接口。在对应的 compute
方法 实现自身策略的计费逻辑。
private static double getResult(long money, int type) { double result = money; if (money >= 1000) { if (type == UserType.SILVER_VIP.getCode()) { result = new SilverStrategy().compute(money); } else if (type == UserType.GOLD_VIP.getCode()) { result = new GoldStrategy().compute(money); } else if (type == UserType.PLATINUM_VIP.getCode()) { result = new PlatinumStrategy().compute(money); } else { result = new OrdinaryStrategy().compute(money); } } return result; } 复制代码
然后对应 getResult
方法,根据 type
替换为对应的 用户VIP 策略
。
这里代码上出现了重复的调用 compute
,我们可以尝试进一步优化。
private static double getResult(long money, int type) { if (money < 1000) { return money; } Strategy strategy; if (type == UserType.SILVER_VIP.getCode()) { strategy = new SilverStrategy(); } else if (type == UserType.GOLD_VIP.getCode()) { strategy = new GoldStrategy(); } else if (type == UserType.PLATINUM_VIP.getCode()) { strategy = new PlatinumStrategy(); } else { strategy = new OrdinaryStrategy(); } return strategy.compute(money); } 复制代码
还记得我在第一篇中说到的 卫语句
吗? 我们在这里把 money < 1000 的情况提前 return。更关注于 满1000逻辑
,也可以减少不必要的缩进。
我曾一度 以为 策略模式不过如此。以为代码优化到这已经可以了。
但是还有一个恐怖的事情, if-else
依然存在 :)
我尝试翻阅了许多书籍,查看如何消除 策略模式中的 if-else
书中大部分的方法是,使用简单工厂 + 策略模式。把 if - else
切换为 switch
创建一个工厂方法而已。
但是这远远没有达到我想要的效果,打倒 if - else
直到某一天夜里,我大佬在群里分享一个 Java8
小技巧时,从此大开新世界。
public interface Strategy { double compute(long money); // 返回 type int getType(); } public class OrdinaryStrategy implements Strategy { @Override public double compute(long money) { System.out.println("普通会员 不打折"); return money; } // 添加 type 返回 @Override public int getType() { return UserType.SILVER_VIP.getCode(); } } public class SilverStrategy implements Strategy { @Override public double compute(long money) { System.out.println("白银会员 优惠50元"); return money - 50; } // type 返回 @Override public int getType() { return UserType.SILVER_VIP.getCode(); } } ....省略剩下 Strategy 复制代码
我们先在 Strategy 新增一个 getType
方法,用来 标示
该策略的 type
值。代码相对简单,这里就不过多介绍了
public class StrategyFactory { private Map<Integer, Strategy> map; public StrategyFactory() { List<Strategy> strategies = new ArrayList<>(); strategies.add(new OrdinaryStrategy()); strategies.add(new SilverStrategy()); strategies.add(new GoldStrategy()); strategies.add(new PlatinumStrategy()); strategies.add(new PlatinumStrategy()); // 看这里 看这里 看这里! map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy)); /* 等同上面 map = new HashMap<>(); for (Strategy strategy : strategies) { map.put(strategy.getType(), strategy); }*/ } public static class Holder { public static StrategyFactory instance = new StrategyFactory(); } public static StrategyFactory getInstance() { return Holder.instance; } public Strategy get(Integer type) { return map.get(type); } } 复制代码
静态内部类单例,单例模式实现的一种,不是本文重点,如不了解,可以自行 google
我们再着手创建一个 StrategyFactory
工厂类。StrategyFactory 这里我使用的是 静态内部类单例
,在构造方法的时候,初始化好 需要的 Strategy
,并把 list
转化为 map
。
这里 转化就是 “灵魂”
所在。
我们先来看看 Java8
语法中的小技巧。
通常情况下,我们遍历 List,手动 put
到 Map 中。
-------------- before ----------------- map = new HashMap<>(); for (Strategy strategy : strategies) { map.put(strategy.getType(), strategy); } -------------- after Java8 ----------------- map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy)); 复制代码
toMap
第一个参数是一个Function,对应 Map 中的 key
,第二个参数也是一个Function,strategy -> strategy, 左边 strategy
是遍历 strategies 中的每一个strategy,右边 strategy
则是 Map 对应 value
值。
若是不了解 Java8
语法的朋友,强烈建议看 《 Java8 实战
》,书中详细的介绍了 Lambda
表达式、 Stream
等语法。
private static double getResult(long money, int type) { if (money < 1000) { return money; } Strategy strategy = StrategyFactory.getInstance().get(type); if (strategy == null){ throw new IllegalArgumentException("please input right type"); } return strategy.compute(money); } 复制代码
至此,通过一个工厂类,在我们在 getResult()
调用的时候,根据传入 type
,即可获取到 对应 Strategy
再也没有可怕的 if-else
语句。
完结撒花撒花 : )
后续代码优化上,若是 Java 项目,可以尝试使用 自定义注解
,注解 Strategy 实现类。
这样可以简化原来需在工厂类 List 添加一个 Stratey 策略
。
以上就是我在开发中遇到复杂的 if-else
语句 “优雅处理”
思路,如有不妥,欢迎大家一起交流学习。