

[解锁新姿势] 回想起被 `if-else` 支配的恐惧,我们要打倒 if - else
source link: https://juejin.im/post/5def654f51882512302daeef
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[解锁新姿势] 回想起被 `if-else` 支配的恐惧,我们要打倒 if - else
[解锁新姿势] 兄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等级,计算出用户最终的费用。
- 普通会员 不打折
- 白银会员 优惠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
依然存在 :)
我尝试翻阅了许多书籍,查看如何消除 策略模式中的 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);
}
}
复制代码
静态内部类单例,单例模式实现的一种,不是本文重点,如不了解,可以自行
我们再着手创建一个 StrategyFactory
工厂类。StrategyFactory 这里我使用的是静态内部类单例
,在构造方法的时候,初始化好 需要的 Strategy
,并把 list
转化为 map
。
这里 转化就是“灵魂”
所在。
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));
复制代码
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
语句“优雅处理”
思路,如有不妥,欢迎大家一起交流学习。
Recommend
-
25
不知不觉被改变。
-
21
解锁Java打包新姿势 在平时我们打包会将其打成Jar,那么在其他平台运行的时候就需要安装jre来支持运行。 那么实际上Java是可以打包成native平台所属类型的,例如: installer image ...
-
25
1. 引入 在0.5.1版本之前,用户若想删除某条记录,可以使用Spark DataSource,并将 DataSourceWriteOptions.PAYLOAD_CLASS_OPT_KEY 设置为 EmptyHoodieReco...
-
4
被“敏感词”支配的恐惧!如何快速恢复变狗listing?-跨境头条-AMZ123亚马逊导航-跨境电商出海门户 被“敏感词”支配的恐惧!如何快速恢复变狗listing?...
-
4
被亚马逊客服支配的恐惧?卖家差点气到吐血,被逼无奈号召联名投诉! ...
-
7
那一天,我被Redis主从架构支配的恐惧面试官:要不你来讲讲你最近在看的点呗?可以拉出来一起讨论下(今天我也不知道要问什么)候选者:最近在看...
-
5
编辑导语:在这个互联网时代,似乎颜值越来越成为一个大众标准,甚至许多人都打着“颜值即正义”的旗号。颜值也不再局限于对人的评判,还延伸到了各种产品包装上。本文作者根据自己的工作经历,提出了“打道颜值即正义”的观点,来跟大家聊聊产品包装的...
-
9
看“颜值”,是很多人买东西的首要标准。对这部分人而言,产品的功能似乎没有颜值重要。因此,有些品牌利用这种心理,打出“颜值即正义”的口号给用户洗脑,用户也不知情的跳入陷阱。本文作者用自己的经验,提出打倒“颜值即正义”的看法,让用户跳出产品...
-
10
次世代主机战争翻篇,订阅制最终要打倒的是微软还是索尼?竞核·2022-06-22 12:30现在来看,还是“巨硬”更硬些要寻找下...
-
2
颠覆GPU、打倒英伟达!深扒12家AI芯片独角兽 常嘉帅 发表于 2024年01月10日 09:55 ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK