点击上方“方志朋”,选择“设为星标”

  • 回复”666“获取新整理的面试资料

作者:hyzhan43

juejin.im/post/5def654f51882512302daeef

前言

在之前文章说到,简单 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 语句“优雅处理”思路,如有不妥,欢迎大家一起交流学习。

热门内容:

  • HttpClient连接池设置引发的一次雪崩

  • SpringBoot 整合 Shiro 实现动态权限加载更新+ Session 共享 + 单点登录

  • 老弟,你连HTTPS 原理都不懂,还给我讲“中间人攻击”,逗我吗...

  • Mybatis:颠覆你心中对事务的理解

  • 为什么强烈推荐 Java 程序员使用 Google Guava 编程!

  • IntelliJ IDEA 快捷键终极大全,速度收藏!

  • 「Jenkins+Git+Maven+Shell+Tomcat持续集成」经典教程  

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

解锁新姿势:探讨复杂的 if-else 语句“优雅处理”的思路相关推荐

  1. [解锁新姿势] 兄dei 我感觉你在写bug

    前言: 继上篇 [解锁新姿势] 兄dei,你代码需要优化了 介绍一些代码的优化的小技巧. 但是我们除了在代码编写上需要优雅, 还需要编写对应的测试用例, 以此来保证代码的质量. 在这篇我们继续在学习如 ...

  2. android解锁win,Win10电脑解锁新姿势:WP/安卓手机、微软手环当钥匙

    IT之家讯 微软在官方网站公布了Win10的开发路线图,其中描述了目前已经实现的功能.正在预览测试以及正在开发中的功能.根据描述,微软正在开发一种全新的Win10电脑解锁方式. 首先,你可以使用自己的 ...

  3. [解锁新姿势] 回想起被 `if-else` 支配的恐惧,我们要打倒 if - else

    前言 [解锁新姿势] 兄dei,你代码需要优化了 在之前文章说到,简单 if-else,可以使用 卫语句 进行优化.但是在实际开发中,往往不是简单 if-else 结构,我们通常会不经意间写下如下代码 ...

  4. windows编程 识别拖动_Quicker 解锁新姿势!Windows 还能这么用?

    不用记住软件复杂的快捷方式,轻轻按下鼠标滚轮,便可唤出当前软件的专属工具箱,一键启动原本要多次点击鼠标的操作,这样的时刻是否很美妙? Excel 选中表格数据加黑框并横向打印,一键搞定 Quicker ...

  5. 自定义控件从入门到轻生之---解锁新姿势

    所有blog局限于博主水平有限,很多不足之处大家可以指出共同探讨进步. 尊重原创转载请注明:From 倪大叶http://blog.csdn.net/renyi0109 侵权必究!虽然我不知道具体怎么 ...

  6. [解锁新姿势] 兄dei,你代码需要优化了

    黑客(程序员)也是创作者,与画家.建筑师.作家一样. --<黑客与画家> 前言 在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题.甚至有些复制粘贴过来,不加 ...

  7. 解锁新姿势,揭秘如何通过公众号迁移方式给自己的爱号开通留言板功能

    关注我的公众号:帅哥趣谈, 公众号首先发表. 引言 玩公众号有一段时间了,很是羡慕别人的留言板功能,一心想着给自己的号也整一个.淘宝一问价格在2000千左右,又望而却步了.不整吧,又老是想着,情绪的拉 ...

  8. [解锁新姿势] 兄dei,你代码需要优化了

    前言 在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题.甚至有些复制粘贴过来,不加以整理规范.往往导致项目后期难以维护,更别说后续接手项目的人.所以啊,我们要编写出优雅 ...

  9. [解锁新姿势] 优化参数前置校验

    前言 我们通常写接口都会用到 @Valid 注解,通过 @NotNull,@NotEmpty 等等来简单校验我们的接口入参. 但是有些入参,需要查询数据库,这时候 @Valid 自带的校验注解,就满足 ...

最新文章

  1. mysql出现ERROR1698(28000):Access denied for user root@localhost错误解决方法
  2. atom 主板 文件服务器,小巧而精悍!Atom工控主板平台拆解测试
  3. java冒泡排序_Java算法分析之冒泡排序(Bubble Sort)
  4. python 第三方库 工具
  5. 关于photoshop处理图片的自动化
  6. SmartUpload上传下载及文件名和文件内容中文问题
  7. 公司僵尸帐号引发了一系列的入侵事件-细说密码强度验证的重要性
  8. POJ-3154-Graveyard
  9. 【Linux】使用ZStack私有云创建本地Linux服务器
  10. dnf时装预览怎么打开_DNF最疯狂的年代,纯色天空整套白给,“神话”宠物不卖只送...
  11. 模型有锯齿_小雕课堂 | 最好的抗锯齿,画质保留提升帧数
  12. IOS 文件管理 2
  13. Android7.1编译SDK报错解决方法总结
  14. 关于Vista的AppInfo服务被禁的问题
  15. svn命令行回滚到指定版本
  16. 滑动切换下一个视频,点击暂停视频,再次点击播放视频,很多案列pc预览正常,真机调试就不能用了;此案例我手机是可以用的,废话不多说直接上源码
  17. html caption属性的值,HTML中的caption属性是什么意思?caption标签在HTML中
  18. uview ui与element ui的区别和用法
  19. 图片木马制作方法详细教程
  20. bzoj4816: [Sdoi2017]数字表格

热门文章

  1. c/c++ 拷贝控制 构造函数的问题
  2. 前端相关html和css
  3. MyEclipse10整合Axis2插件
  4. JAVA如何检测GC日志
  5. Matlab数据的可视化 -- 柱形立体图
  6. 【ACM】二叉搜索树(Binary Search Tree /BS Tree) 小结
  7. 【HDU】1305 Immediate Decodability(字典树:结构体数组,二维数组,链表/指针)
  8. 在 Python 中妙用短路机制
  9. Google和网易有道合作开课了
  10. 教你如何编写第一个爬虫