前言
对于简单 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 等级,计算出用户最终的费用。
- 普通会员:不打折
- 白银会员:优惠50元
- 黄金会员:打8折
- 白金会员:优惠50元,再打7折
编码实现
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
依然存在。
工厂 + 策略
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));
}
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 语法的朋友,强烈建议看《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
语句。