不是我吹牛逼,这绝对是去掉 if...else 最佳的文章

我相信小伙伴一定看过多篇怎么去掉 if…else 的文章,也知道大家都很有心得,知道多种方法来去掉 if…else ,比如 Option,策略模式等等,但我相信大明哥这篇文章绝对是最全,最完备怎么去掉 if…else 的文章,里面有些方法我相信有小伙伴肯定不知道,我也不卖关子,直接进入主题,如何干掉 if…else。

方法一:提前 return

假如有如下代码:

if (condition){doSomething;
} else {return;
}

这种代码我们一般采用提前 return 的方式,去掉不必要的 else。

if (!condition){return
}doSomething;

这种方法一般只适合分支结构很简单的 if…else,我们可以提前 return ,把一些不必要的 if…else 去掉。

方法二:枚举

枚举其实也是可以去掉 if…else 的,如下:

String orderStatusDes;
if ("1".equals(orderStatus)) {orderStatusDes = "订单未支付";
} else if ("2".equals(orderStatus)) {orderStatusDes = "订单已支付";
} else if ("3".equals(orderStatus)) {orderStatusDes = "订单已发货";
} else if ("4".equals(orderStatus)) {orderStatusDes = "订单已签收";
} else if ("5".equals(orderStatus)) {orderStatusDes = "订单已评价";
}

可能有小伙伴说,靠,谁会写这种代码?别这么绝对,大明哥工作这么久了,到现在依然看到有工作 5 、6 年的人写这样的代码。这种类型的代码非常适合枚举来解决。

先定义一个枚举类:

@Getter
@AllArgsConstructor
public enum OrderStatusEnum {UN_PAID("1","订单未支付"),PAIDED("2","订单已支付"),SENDED("3","订单已发货"),SINGED("4","订单已签收"),EVALUATED("5","订单已评价");private String status;private String statusDes;static OrderStatusEnum of(String status) {for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) {if (statusEnum.getStatus().equals(status)) {return statusEnum;}}return null;}
}

有了这个枚举,上面代码直接可以优化为一行代码:

String orderStatusDes = OrderStatusEnum.of(orderStatus).getStatusDes();

当然一般在实际项目中,这种处理方式也不是最佳的,最佳的方式应该是在数据库里面有一个码值配置表,然后加载到系统缓存中来,在通过 code 去取值。当然枚举也是一种很好的解决方案。

方案三:Optional 判空

我相信各位小伙伴的项目里面一定存在非空判断,如果为空,则抛出异常或者 return。

Order order = getOrderById(id);
if (order == null) {return "-1";
} else {return order.getOrderStatus();
}

对于这种代码我们利用 Optional 可以非常优雅地解决。

return Optional.ofNullable(order).map(o -> o.getOrderStatus()).orElse("-1");

这种方式是不是非常优雅,有格调。最后补充一句:

防止 NPE,是程序员的基本修养

方案四:表驱动法

表驱动法,是一种让你可以在表中查找信息,而不必用过多的 if…else 来把他们找出来的方法。如下:

if ("code1".equals(action)) {doAction1();
} else if ("code2".equals(action)) {doAction2();
} else if ("code3".equals(action)) {doAction3();
} else if ("code4".equals(action)) {doAction4();
} else if ("code5".equals(action)) {doAction5();
}

优化方法如下:

Map<String, Function<?> action> actionMap = new HashMap<>();
action.put("code1",() -> {doAction1()});
action.put("code2",() -> {doAction2()});
action.put("code3",() -> {doAction3()});
action.put("code4",() -> {doAction4()});
action.put("code5",() -> {doAction5()});// 使用
actionMap.get(action).apply();

其实这种方式也不是很好,因为它会显得代码非常臃肿。一种变形方案是将 doAction() 抽象成类。如下:

//1. 先定义一个 ActionService 接口
public interface ActionService {void doAction();
}//2. 然后定义 5 个实现类
public class ActionService1 implements ActionService{public void doAction() {//do something}
}//3. 加入表中
Map<String, ActionService> actionMap = new HashMap<>();
action.put("code1",new ActionService1());
action.put("code2",new ActionService2());
action.put("code3",new ActionService3());
action.put("code4",new ActionService4());
action.put("code5",new ActionService5());//4. 调用
actionMap.get(action).doAction();

这种方式是不是比较优雅些!

方案五:策略模式 + 工厂方法

策略模式 + 工厂方法是解决 if…else 用得非常多的方案,它和上面的表驱动法有点儿类似。使用策略模式 + 工厂方法分为几个步骤,以上面例子为例:

  • 把条件模块抽象为一个公共的接口,策略接口
public interface ActionService {void doAction();
}
  • 根据每个逻辑,定义出自己具体的策略实现类,如下:
public class ActionService1 implements ActionService{public void doAction() {//do something}
}public class ActionService2 implements ActionService{public void doAction() {//do something}
}// 省略其他策略
  • 工厂类,统一调度,用来管理这些策略,如下:
public class ActionServiceFactory {private ActionServiceFactory(){}private static class SingletonHolder{private static ActionServiceFactory instance=new ActionServiceFactory();}public static ActionServiceFactory getInstance(){return SingletonHolder.instance;}private static final Map<String,ActionService> ACTION_SERVICE_MAP = new HashMap<String, ActionService>();static {ACTION_SERVICE_MAP.put("action1",new ActionService1());ACTION_SERVICE_MAP.put("action2",new ActionService2());ACTION_SERVICE_MAP.put("action3",new ActionService3());ACTION_SERVICE_MAP.put("action4",new ActionService4());ACTION_SERVICE_MAP.put("action5",new ActionService5());}public static ActionService getActionService(String actionCode) {ActionService actionService = ACTION_SERVICE_MAP.get(actionCode);if (actionService == null) {throw new RuntimeException("非法 actionCode");}return actionService;}public void doAction(String actionCode) {getActionService(actionCode).doAction();}
}

单例模式实现工厂类。

  • 使用
ActionServiceFactory.getInstance().doAction("action1");

这种优化方式也是很优雅的,特别适合分支较多,逻辑较为复杂的代码块,这种方式将分支逻辑与业务代码解耦了,是一种很不错的方案。

方案六:责任链模式

你想不到责任链模式也能优化 if…else 吧。责任链我们可以看做是一个单链表的数据结构,一个对象一个对象地过滤条件,符合的就执行,然后结束,不符合的就传递到下一个节点,如果每个对象都无法处理,一般都有一个最终的节点来统一处理。

我们依然以上面那个例子为例。

  • 定义责任链处理请求节点
public abstract class ActionHandler {// 后继节点protected ActionHandler successor;/*** 处理请求* @param actionCode*/public void handler(String actionCode) {doHandler(actionCode);}// 设置后继节点protected ActionHandler setSuccessor(ActionHandler successor) {this.successor = successor;return this;}// 处理请求public abstract void doHandler(String actionCode);
}
  • 定义首尾节点,用于一些异常情况的处理
// 首节点,判断 actionCode 是否为空
public class HeadHandler extends ActionHandler{@Overridepublic void doHandler(String actionCode) {if (StringUtils.isBlank(actionCode)) {throw new RuntimeException("actionCode 不能为空");}successor.doHandler(actionCode);}
}// 尾节点,直接抛出异常,因为到了尾节点说明当前 code 没有处理
public class TailHandler extends ActionHandler{@Overridepublic void doHandler(String actionCode) {throw new RuntimeException("当前 code[" + actionCode + "] 没有具体的 Handler 处理");}
}
  • 定义各个节点具体的实现节点
public class ActionHandler1 extends ActionHandler{@Overridepublic void doHandler(String actionCode) {if ("action1".equals(actionCode)) {doAction1();} else {// 传递到下一个节点successor.doHandler(actionCode);}}
}public class ActionHandler2 extends ActionHandler{@Overridepublic void doHandler(String actionCode) {if ("action2".equals(actionCode)) {doAction2();} else {// 传递到下一个节点successor.doHandler(actionCode);}}
}// 省略其他节点
  • 定义工厂,来构建一条完整的责任链,并负责调度
public class ActionHandlerFactory {private ActionHandler headHandler;private ActionHandlerFactory(){headHandler = new HeadHandler();ActionHandler actionHandler1 = new ActionHandler1();ActionHandler actionHandler2 = new ActionHandler2();ActionHandler actionHandler3 = new ActionHandler3();ActionHandler actionHandler4 = new ActionHandler4();ActionHandler actionHandler5 = new ActionHandler5();ActionHandler tailHandler = new TailHandler();// 构建一条完整的责任链headHandler.setSuccessor(actionHandler1).setSuccessor(actionHandler2).setSuccessor(actionHandler3).setSuccessor(actionHandler4).setSuccessor(actionHandler5).setSuccessor(tailHandler);}private static class SingletonHolder{private static ActionHandlerFactory instance=new ActionHandlerFactory();}public static ActionHandlerFactory getInstance(){return SingletonHolder.instance;}public void doAction(String actionCode) {headHandler.doHandler(actionCode);}
}
  • 使用
ActionHandlerFactory.getInstance().doAction("action1");

方案七:Function

Function 是 Java 8 中的函数式接口,利用好它我们可以极大地简化我们的代码,例如利用它我们可以轻松去掉我们的 if…else。比如有下面一段代码:

// 抛出异常
if (...) {throw new RuntimeException("哎呀,有异常哦...")
}// if...else 分支
if(...) {doSomething1();
} else {doSomething2();
}

现在我们利用 Function 来处理上面两段代码

处理抛出异常

  • 定义抛出异常的形式的函数式接口
@FunctionalInterface
public interface ThrowExceptionFunction {/*** 抛出异常* @param message*/void throwMessage(String message);
}

这里只需要有一个这样的函数式接口就行,而且方法也没有返回值,是一个消费型接口。

  • 增加判断工具类
public class ValidateUtils {/*** 抛出异常* @param flag* @return*/public static ThrowExceptionFunction isTrue(Boolean flag) {return (errorMessage) -> {if (flag) {throw new RuntimeException(errorMessage);}};}
}

ValidateUtils 类也是非常简单的,如果传入的 flag 为 true,则抛出异常。isTrue() 返回值也是刚刚我们定义的 ThrowExceptionFunction。

  • 使用
ValidateUtils.isTrue(flag).throwMessage("哎呀,有异常哦...");

使用方式是不是非常简单?

处理 if…else 分支

其实使用 Function 来去掉 if…else 分支我认为有点儿偏门,因为它非常依赖我们定义的 Function 函数,比如我们定义的方法只有两个参数,那它就只能处理处理两个分支的,对于三个分支的 if…else 则需要重新定义方法。下面以两个分支为例。

  • 定义函数式接口
@FunctionalInterface
public interface ActionHandler {void doActionHandler(ActionService trueActionService,ActionService falseActionService);
}

函数式接口中定义了一个方法,doActionHandler(),它有两个参数,分别为:

  1. trueActionService:为 true 时要进行的操作
  2. falseActionService:为 false 时要进行的操作
  • 定义判断方法

增加一个工具类,用来判断为 true 时执行哪个方法,为 false 时执行哪个方法。

public class ActionHandlerUtils {public static ActionHandler isTrue(Boolean flag) {return (trueActionService,falseActionService) -> {if (flag) {trueActionService.doAction();} else {trueActionService.doAction();}};}
}
  • 使用
ActionHandlerUtils.isTrue(true).doActionHandler(() -> {//do true Something},() ->{//do false Something});

总结

大明哥在这里总结了 7 中方式用来解决 if…else 的问题,我相信里面总有一两种方案是你比较满意的,七种方案各有优劣,各自有各自的使用场景,我们需要在实践中不断领悟,在重构中不断进化,总结出适合自己最佳的重构方案。

重构之路,任重而道远,各位其行且珍惜

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/157788.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux学习笔记】进程概念(中)

1. 操作系统的进程状态2. Linux操作系统的进程状态3. 僵尸进程4. 孤儿进程5. 进程优先级5.1. 优先级是什么和为什么要有优先级5.2. Linux中的进程优先级 6. 进程切换7. 环境变量7.1. 环境变量的认识7.2. 环境变量相关的命令7.3. 环境变量和本地变量7.4. 命令行参数7.5. 获取环境…

JVM 分代垃圾回收过程

堆空间划分了代&#xff1a; 年轻代&#xff08;Young Generation&#xff09;分为 eden 和 Survivor 两个区&#xff0c;Survivor 又分为2个均等的区&#xff0c;S0 和 S1。 首先&#xff0c;新对象都分配到年轻代的 eden 空间&#xff0c;Survivor 刚开始是空的。 当 eden …

IDEA新建maven项目,使用mybatis操作数据库完整过程

IDEA新建maven项目&#xff0c;使用mybatis操作数据库完整过程 一、IDEA新建maven项目二、配置mybatis三、创建表对应实体类四、创建mapper接口五、使用mybatis操作数据库 前提&#xff1a; 这个教程是在maven项目中使用mybatis进行数据库操作&#xff0c;不是在spring boot项目…

精准测试:提高软件质量和用户满意度的利器

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

目标检测:Proposal-Contrastive Pretraining for Object Detection from Fewer Data

论文作者&#xff1a;Quentin Bouniot,Romaric Audigier,Anglique Loesch,Amaury Habrard 作者单位&#xff1a;Universit Paris-Saclay; Universit Jean Monnet Saint-Etienne; Universitaire de France (IUF) 论文链接&#xff1a;http://arxiv.org/abs/2310.16835v1 内容…

用rust写web服务器笔记(11/1)

文章目录 一、创建一个具有监听链接功能web服务器二、读取请求内容三、编写web服务器返回网页(编写响应)四、编写web服务器有条件的返回网页五、编写多线程的web服务器六、用线程池实现web服务器七、实现线程池清除的web服务器八、读取文件 rust官网文档地址&#xff1a;https:…

学习笔记|两独立样本秩和检验|曼-惠特尼 U数据分布图|规范表达|《小白爱上SPSS》课程:SPSS第十二讲 | 两独立样本秩和检验如何做?

目录 学习目的软件版本原始文档两独立样本秩和检验一、实战案例二、统计策略三、SPSS操作1、正态性检验2、两样本秩和检验 四、结果解读疑问&#xff1a;曼-惠特尼 U数据分布图如何绘制&#xff1f; 五、规范报告1、规范表格2、规范文字 六、划重点 学习目的 SPSS第十二讲 | 两…

频谱仪超外差和零中频架构

文章目录 超外差结构零中频结构接收机结构发射机结构 优缺点对比附录相关词汇多次变频的形象解释 参考文献 频谱仪的本质就是一个超宽带、超宽调谐范围、高动态范围的通信接收机&#xff0c; 频谱仪的原理即通信接收机的原理。 遇到高频率高带宽谐波成分复杂的通信信号的话&am…

关于idea使用的一些操作设置

关于idea使用的一些操作设置 1. 常用的一下设置1.1 快捷键相关1.2 配置自动生成注释&#xff08;类、方法等&#xff09;1.3 maven项目相关1.4 常见其他的一些操作设置 2. IntelliJ IDEA 取消param注释中参数报错提示3. idea同时打开多个文件&#xff0c;导航栏不隐藏、自动换行…

AI时代新宠,向量数据库

随着生成式人工智能&#xff08;GAI&#xff09;应用以及大语言模型&#xff08;LLM&#xff09;的快速发展&#xff0c;一种新型数据库也获得了市场和资本的重点关注&#xff0c;它就是向量数据库&#xff08;Vector Database&#xff09;。 向量数据库简介 向量数据库是一种…

微信小程序自定义弹窗阻止滑动冒泡catchtouchmove之后弹窗内部内容无法滑动

自定义弹窗 如图所示&#xff1a; 自定义弹窗内部有带滚动条的盒子区域 问题&#xff1a; 在盒子上滑动&#xff0c;页面如果超出一屏的话&#xff0c;也会跟着一起上下滚动 解决方案&#xff1a;给自定义弹窗 添加 catchtouchmove 事件&#xff0c;阻止冒泡即可 网上不少…

【C++】C++11【上】列表初始化|声明|新容器|右值引用|完美转发|新的类功能

目录 1、 C11简介 2、 统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 3、声明 3.1 auto和范围for 3.1decltype 3.3 nullptr 4、新容器 5、 右值引用 5.1左值引用和右值引用 5.2 左值引用与右值引用比较 5.3 左值和右值引用使用场景及意义 6…