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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 消除代码中的 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的安全审计。
相关文章
|
自然语言处理 监控 Cloud Native
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
1709 0
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
|
负载均衡 Java API
Java一分钟之-Spring Cloud OpenFeign:声明式服务调用
【6月更文挑战第9天】Spring Cloud OpenFeign是声明式服务调用库,简化了微服务间调用。通过动态代理,它允许开发者用Java接口调用HTTP服务,支持服务发现、负载均衡。本文介绍了OpenFeign的基本概念,展示了如何添加依赖、开启客户端和定义服务接口。还讨论了接口调用失败、超时重试和日志配置等问题及其解决方案,并提供了自定义Feign配置的代码示例。通过学习,读者可以更好地在微服务架构中使用OpenFeign进行服务通信。
576 4
|
运维 负载均衡 监控
提升系统性能:高效运维的秘密武器——负载均衡技术
在当今数字化时代,系统的高可用性和高性能成为各类企业和组织追求的目标。本文旨在探讨负载均衡技术在运维工作中的关键作用,通过深入分析其原理、类型及实际应用案例,揭示如何利用这项技术优化资源分配,提高系统的响应速度和可靠性,确保用户体验的稳定与流畅。无论是面对突如其来的高流量冲击,还是日常的运维管理,负载均衡都展现出了不可或缺的重要性,成为现代IT架构中的基石之一。
616 4
|
机器学习/深度学习 自然语言处理 机器人
基于深度学习的智能语音机器人交互系统设计方案
**摘要** 本项目旨在设计和实现一套基于深度学习的智能语音机器人交互系统,该系统能够准确识别和理解用户的语音指令,提供快速响应,并注重安全性和用户友好性。系统采用分层架构,包括用户层、应用层、服务层和数据层,涉及语音识别、自然语言处理和语音合成等关键技术。深度学习模型,如RNN和LSTM,用于提升识别准确率,微服务架构和云计算技术确保系统的高效性和可扩展性。系统流程涵盖用户注册、语音数据采集、识别、处理和反馈。预期效果是高识别准确率、高效处理和良好的用户体验。未来计划包括系统性能优化和更多应用场景的探索,目标是打造一个适用于智能家居、医疗健康、教育培训等多个领域的智能语音交互解决方案。
|
存储 网络协议 Unix
图解VirtualBox安装CentOS 7
VirtualBox是由德国InnoTek软件公司出品的虚拟机软件,现在则由甲骨文公司进行开发,是甲骨文公司xVM虚拟化平台技术的一部分。
2848 0
图解VirtualBox安装CentOS 7
|
小程序 数据可视化 数据库
云开发(微信-小程序)笔记(十七)---- cms(内容管理)及案例
云开发(微信-小程序)笔记(十七)---- cms(内容管理)及案例
721 0
|
机器学习/深度学习 存储 文字识别
基于ERNIELayout&pdfplumber-UIE的多方案学术论文信息抽取
基于ERNIELayout&pdfplumber-UIE的多方案学术论文信息抽取,小样本能力强悍,OCR、版面分析、信息抽取一应俱全。
|
SQL Oracle Java
Java java jdbc thin远程连接并操作Oracle数据库
Java java jdbc thin远程连接并操作Oracle数据库
210 0
|
存储 SQL 机器学习/深度学习
解决“卡脖子”问题,谁在助力大数据基础软件国产化?
“数字化”是当今社会最先进和最具穿透力的生产力,近十年保持高速发展。围绕“数字化”构筑的数字经济不仅呈现蓬勃发展态势,而且对经济社会发展的贡献越来越大。
993 0
解决“卡脖子”问题,谁在助力大数据基础软件国产化?