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 语句“优雅处理”思路。如有不妥,欢迎大家一起交流学习。
需求
假设有这么一个需求:
一个电商系统,当用户消费满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 {@Overridepublic double compute(long money) {System.out.println("普通会员 不打折");return money;}
}// 白银会员策略
public class SilverStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("白银会员 优惠50元");return money - 50;}
}// 黄金会员策略
public class GoldStrategy implements Strategy{@Overridepublic double compute(long money) {System.out.println("黄金会员 8折");return money * 0.8;}
}// 白金会员策略
public class PlatinumStrategy implements Strategy {@Overridepublic 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);// 返回 typeint getType();
}public class OrdinaryStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("普通会员 不打折");return money;}// 添加 type 返回@Overridepublic int getType() {return UserType.SILVER_VIP.getCode();}
}public class SilverStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("白银会员 优惠50元");return money - 50;}// type 返回@Overridepublic 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。
这里 转化就是“灵魂”所在。
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 语句“优雅处理”思路
if-else 语句优雅处理相关推荐
- 解锁新姿势:探讨复杂的 if-else 语句“优雅处理”的思路
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:hyzhan43 juejin.im/post/5def65 ...
- 一条简单的更新语句,MySQL是如何加锁的?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Java_老男孩 来源:https://urlify.cn/ ...
- 如何优雅地根治null值引起的Bug!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:https://lrwinx.github.io 写在前面 ...
- SQL SELECT INTO语句
This article will cover the SQL SELECT INTO statement including syntax, parameters and use with mult ...
- SpringBoot+RabbitMQ ,保证消息100%投递成功并被消费(附源码)
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:rrd.me/f2cxz 一.先扔一张图 说明: 本文涵盖了 ...
- 这次终于不再为 iptables 犯迷糊了!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 基础概念 linux 的包过滤功能,即 linux 防火墙,它由 ...
- 面试官问:你来讲下Netty通信中的粘包、拆包?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Java技术剑 来源:https://urlify.cn/I ...
- Mybatis 使用的 9 种设计模式,真是太有用了
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:蚂蚁学Python crazyant.net/2022.ht ...
- 写一手好 SQL 很有必要
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:编码砖家 cnblogs.com/xiaoyangjia/p ...
最新文章
- Ruby on rails环境和开发工具准备...
- 【Android FFMPEG 开发】FFMPEG 获取 AVStream 音视频流 ( AVFormatContext 结构体 | 获取音视频流信息 | 获取音视频流个数 | 获取音视频流 )
- jvm八:接口初始化规则
- 【算法系列之万字总结常用的查找算法,持续补充更新中】
- 函数分组学通MongoDB——第三天 细说高级操作
- java设置小数位数_java设置小数点后位数
- 在MySQL中使用正则表达式
- php arcsin,三角函数在线计算器
- 查看App应用签名工具
- php获取图片格式(图片后缀)
- [转]Discuz 模板原理分析
- iTunes现在已在Microsoft Store中
- java poi 生成excel_利用POI生成EXCEL文件的方法实例
- 数学之美:数学究竟是如何被运用到生活中的?
- label-studio批量上传本地数据
- 犀牛插件开发-基础核心-技术概览-总体架构-教程
- 软硬兼济,施耐德电气加速工业数字化转型
- 架构师与算法工程师,谁更......
- JS中location的用法和作用
- 腾讯qq2014官方正式版 v6.3.12390 免费版
热门文章
- xftp、使用pure-ftpd搭建FTP服务
- Qt串口通信-qextserialport
- 黑莓BlackBerry之Helloworld
- 【EFI系统分区】关于EFI系统分区的说明
- .text‘ will not fit in region `ROM‘, region `ROM‘ overflowed by 3056 bytes问题记录
- OrCAD利用Excel制作多引脚复杂元件的原理图库
- cmail 在redhat5.4 x64位安装
- vc中c语言多线程,VS2010配置C/C++多线程使用环境
- “node“不是内部或外部命令,也不是可运行的程序 或批处理文件
- 安装ADSafe净网大师后出现蓝屏,win10自动修复无法开机的问题