回想起被 `if-else` 支配的恐惧,下定决心我要打倒 if



简单 if-else,可以使用 卫语句 进行优化。但是在实际开发中,往往不是简单 if-else 结构,我们通常会不经意间写下如下代码:

回想起被 `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 语句“优雅处理”思路。如有不妥,欢迎大家一起交流学习。


01 需求

假设有这么一个需求:

一个电商系统,当用户消费满1000 金额,可以根据用户VIP等级,享受打折优惠。

根据用户VIP等级,计算出用户最终的费用。

  • 普通会员 不打折
  • 白银会员 优惠50元
  • 黄金会员 8折
  • 白金会员 优惠50元,再打7折

02 编码实现

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 会进行复杂的逻辑计费。 从功能上来说,基本完成,但是对于我这种有代码洁癖的人来说,代码质量上不忍直视。我们开始着手 优化一下我们的第一版代码吧。

03 思考

看到如上代码,聪明的朋友首先想到的是,这不是典型的策略模式吗?

你可真是个机灵鬼,我们先尝试用策略模式来优化一下代码吧。

04 策略模式

(1)什么是策略模式?

可能有的朋友还不清楚,什么是策略模式。策略模式是定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

比如上述需求,有返利、有打折、有折上折等等。这些算法本身就是一种策略。并且这些算法可以相互替换的,比如今天我想让 白银会员优惠50,明天可以替换为 白银会员打9折。

说了那么多,不如编码来得实在。

(2)编码

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逻辑 ,也可以减少不必要的缩进。

05 深思

我曾一度 以为 策略模式不过如此。以为代码优化到这已经可以了。

但是还有一个恐怖的事情,if-else 依然存在 :)

我尝试翻阅了许多书籍,查看如何消除 策略模式中的 if-else

书中大部分的方法是,使用简单工厂 + 策略模式。把 if - else 切换为 switch 创建一个工厂方法而已。

但是这远远没有达到我想要的效果,打倒 if - else

直到某一天夜里,我大佬在群里分享一个 Java8 小技巧时,从此大开新世界。

06 工厂 + 策略

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> 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);

}

}/<strategy>/<integer>

静态内部类单例,单例模式实现的一种,不是本文重点,如不了解,可以自行 google

我们再着手创建一个 StrategyFactory 工厂类。StrategyFactory 这里我使用的是静态内部类单例,在构造方法的时候,初始化好 需要的 Strategy,并把 list 转化为 map。 这里 转化就是“灵魂”所在。

(1)toMap

我们先来看看 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));

(2)效果

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 语句。

07 后续

后续代码优化上,若是 Java 项目,可以尝试使用自定义注解,注解 Strategy 实现类。

这样可以简化原来需在工厂类 List 添加一个 Stratey 策略。

最后

以上就是我在开发中遇到复杂的 if-else 语句“优雅处理”思路,如有不妥,欢迎大家一起交流学习。


作者:hyzhan43
原文链接:https://juejin.im/post/5def654f51882512302daeef


分享到:


相關文章: