消除代码中的 if-else/switch-case的正确姿势

简介: 消除代码中的 if-else/switch-case的正确姿势

 在很多时候,我们代码中会有很多分支,而且分支下面的代码又有一些复杂的逻辑,相信很多人都喜欢用 if-else/switch-case 去实现。做的不好的会直接把实现的代码放在 if-else/switch-case 的分支之下:

1. switch ( type ) {
2. case case1:
3.         ...
4.         ...
5. break;
6. case case2:
7.         ...
8.         ...
9. break;
10. case case3:
11.         ...
12.         ...
13. break
14. default:
15. return null;
16. }

  这样的代码不仅冗长,读起来也非常困难。做的好一点的会把这些逻辑封装成函数然后在分支中调用:

1. switch ( type ) {
2. case case1:
3. return case1Func();
4. case case2:
5. return case2Func();
6. case case3:
7. return case3Func();
8. default:
9. return null;
10. }

 

即使这样也是面向过程思维的写法,以前写 C 程序的时候也总喜欢这样写,毫无设计模式可言。不仅违背开闭原则,而且随着 switch-case 分支的增多,该段代码只会越来越冗长。其实这种代码已经有成熟的模式去消除诸多的 if-else/switch-case 分支。本文就教大家在 Spring 中如何用注解+策略模式+简单工厂的方式消除 if-else/switch-case 。我们就拿 QQ 空间的个人中心举例子,假如 QQ 空间个人中心有四个 tab 分别是列出我的说说、我的日志、我的照片和我的访客。一般的后台代码很有可能如下:

1. //各个 tab 名称的枚举:
2. public enum UserRelatedType {
3. /**
4.      * 说说
5.      */
6.     SHUOSHUO("说说"),
7. 
8. /**
9.      * 日志
10.      */
11.     RIZHI("日志"),
12. 
13. /**
14.      * 发布
15.      */
16.     ZHAOPIAN("照片"),
17. 
18. /**
19.      * 访客
20.      */
21.     FANGKE("");
22. 
23. private String desc;
24. 
25.     UserRelatedType(String desc) {
26. this.desc = desc;
27.     }
28. 
29. public String getDesc() {
30. return desc;
31.     }
32. 
33. public void setDesc(String desc) {
34. this.desc = desc;
35.     }
36. }

列出 QQ 用户个人中心相关 tab 的代码:

1. public List<UserRelatedVO> listRelated(UserRelatedQuery query){
2. 
3. UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) );
4. switch ( relatedType ) {
5. case SHUOSHUO:
6. return listRelatedShuoshuo( query );
7. case RIZHI:
8. return listRelatedRizhi( query );
9. case ZHAOPIAN:
10. return listRelatedZhaopian( query );
11. case FANGKE:
12. return listRelatedFangke( query );
13. default:
14. return null;
15.     }
16. }

而采用注解+策略模式+简单工厂,重构后代码如下:

1、定义一个注解,用来完全消除 if-else:

1. @Target(ElementType.TYPE)
2. @Retention(RetentionPolicy.RUNTIME)
3. public @interface RelatedTypeAnnotation {
4. /**
5.      * 用户相关类型名称
6.      */
7.     UserRelatedType value();
8. }

2、先定义了个接口,所有 tab 都要实现该接口。其中 list 是 tab 数据展示的方法。

1. public interface UserRelated {
2. 
3. /**
4.      * 列出详细信息
5.      *
6.      * @param query
7.      * @return
8.      */
9.     List<UserRelatedVO> list(UserRelatedQuery query);
10. }

3、定义具体的各个 tab 的实现,继承 UserRelated 策略接口

  • 我的说说
1. @Component("userRelatedShuoshuo")
2. @RelatedTypeAnnotation( value = UserRelatedType.SHUOSHUO )
3. public class UserRelatedShuoshuo implements UserRelated {
4. @Override
5. public List<UserRelatedVO> list(UserRelatedQuery query) {
6.         System.out.println("我的说说!");
7. return list;
8.     }
9. }
  • 我的日志
1. @Component("userRelatedRizhi")
2. @RelatedTypeAnnotation( value = UserRelatedType.RIZHI )
3. public class UserRelatedRizhi implements UserRelated {
4. @Override
5. public List<UserRelatedVO> list(UserRelatedQuery query) {
6.         System.out.println("我的日志!");
7. return list;
8.     }
9. }
  • 我的照片
1. @Component("userRelatedZhaopian")
2. @RelatedTypeAnnotation( value = UserRelatedType.ZHAOPIAN )
3. public class UserRelatedZhaopian implements UserRelated {
4. @Override
5. public List<UserRelatedVO> list(UserRelatedQuery query) {
6.         System.out.println("我的照片!");
7. return list;
8.     }
9. }
  • 我的访客
1. @Component("userRelatedFangke")
2. @RelatedTypeAnnotation( value = UserRelatedType.FANGKE )
3. public class UserRelatedFangke implements UserRelated {
4. @Override
5. public List<UserRelatedVO> list(UserRelatedQuery query) {
6.         System.out.println("我的访客!");
7. return list;
8.     }
9. }
  • 定义注解,用来识别身份。
1. @Target(ElementType.TYPE)
2. @Retention(RetentionPolicy.RUNTIME)
3. public @interface RelatedTypeAnnotation {
4. /**
5.      * 用户相关类型名称
6.      */
7.     UserRelatedType value();
8. }
  • 定义一个从 Spring context 获取 bean 的工具类
1. @Component
2. public class SpringContextUtil implements ApplicationContextAware {
3. 
4. private ApplicationContext context;
5. 
6. public ApplicationContext getContext() {
7. return context;
8.     }
9. 
10. @Override
11. public void setApplicationContext(ApplicationContext context)throws BeansException {
12. this.context = context;
13.     }
14. }
  • 定义一个简单工厂,用来生产各种 tab 对象。
1. @Component
2. public class UserRelatedFactory {
3. 
4. @Autowired
5.     SpringContextUtil springContextUtil;
6. 
7. private static Map<UserRelatedType, UserRelated> userRelatedMap = Maps.newConcurrentMap();
8. 
9. //工厂将 Spring 装配的相关的 Bean 用 Map 保存起来
10. public UserRelatedFactory(){
11.         Map<String, Object> beanMap = springContextUtil.getContext().getBeansWithAnnotation(RelatedTypeAnnotation.class);
12. 
13. for(Object userRelated : beanMap.values()) {
14. RelatedTypeAnnotation annotation = userRelated.getClass().getAnnotation(RelatedTypeAnnotation.class);
15.             userRelatedMap.put(annotation.value(), (UserRelated)userRelated);
16.         }
17.     }
18. 
19. public static UserRelated createRelated(UserRelatedType relatedType) {
20. return userRelatedMap.get( relatedType );
21.     }
22. }
  • 调用的代码(listRelated 会在 controller 中被调用)。
1. public List<UserRelatedVO> listRelated(UserRelatedQuery query){
2. 
3. UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) );
4. UserRelated related = UserRelatedFactory.createRelated( relatedType );
5. if( related != null ) {
6. return related.list( query );
7.     } else {
8. return null;
9.     }
10. }

  重构后的代码如果需要再新增一种 tab,比如我的好友,只需要新增一种类型继承 UserRelated 实现其中的 list,并加上相应的注解即可。

  其实这是一种通用的解决方案,当你 if-else/switch-case 的分支超过 3 个、且分支代码相似且冗长的情况下就应该考虑这种模式。这种模式写出的代码面向对象、清晰、易扩展还高大上,何乐而不为呀,赶紧试试吧!

 

来源:https://juejin.im/post/5ca9f113e51d452b5e458ec3

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
相关文章
|
存储 网络协议 Unix
图解VirtualBox安装CentOS 7
VirtualBox是由德国InnoTek软件公司出品的虚拟机软件,现在则由甲骨文公司进行开发,是甲骨文公司xVM虚拟化平台技术的一部分。
3171 0
图解VirtualBox安装CentOS 7
|
NoSQL Devops 程序员
devops| redis 数据量暴涨进行数据清理实战
redis 作为程序员的 「瑞士军刀」, 在现有业务中扮演着重要的角色. 为了避免触雷, 「保卫世界和平」, 对 redis 数据进行分析并清理.
275 0
devops| redis 数据量暴涨进行数据清理实战
|
Java 容器 设计模式
如何优化代码中大量的if/else,switch/case?
前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单的定义一个表示状态的枚举。
2598 0
|
1天前
|
存储 JavaScript 前端开发
JavaScript基础
本节讲解JavaScript基础核心知识:涵盖值类型与引用类型区别、typeof检测类型及局限性、===与==差异及应用场景、内置函数与对象、原型链五规则、属性查找机制、instanceof原理,以及this指向和箭头函数中this的绑定时机。重点突出类型判断、原型继承与this机制,助力深入理解JS面向对象机制。(238字)
|
2天前
|
安全 数据可视化 网络安全
安全无小事|阿里云先知众测,为企业筑牢防线
专为企业打造的漏洞信息收集平台
1303 2
|
3天前
|
云安全 人工智能
2025,阿里云安全的“年度报告”
拥抱AI时代,阿里云安全为你护航~
1447 2
|
1天前
|
人工智能 自然语言处理 API
n8n:流程自动化、智能化利器
流程自动化助你在重复的业务流程中节省时间,可通过自然语言直接创建工作流啦。
324 3
n8n:流程自动化、智能化利器
|
10天前
|
机器学习/深度学习 安全 API
MAI-UI 开源:通用 GUI 智能体基座登顶 SOTA!
MAI-UI是通义实验室推出的全尺寸GUI智能体基座模型,原生集成用户交互、MCP工具调用与端云协同能力。支持跨App操作、模糊语义理解与主动提问澄清,通过大规模在线强化学习实现复杂任务自动化,在出行、办公等高频场景中表现卓越,已登顶ScreenSpot-Pro、MobileWorld等多项SOTA评测。
1423 7
|
18小时前
|
Linux 数据库
Linux 环境 Polardb-X 数据库 单机版 rpm 包 安装教程
本文介绍在CentOS 7.9环境下安装PolarDB-X单机版数据库的完整流程,涵盖系统环境准备、本地Yum源配置、RPM包安装、用户与目录初始化、依赖库解决、数据库启动及客户端连接等步骤,助您快速部署运行PolarDB-X。
214 1
Linux 环境 Polardb-X 数据库 单机版 rpm 包 安装教程