消除代码中的 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

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
3月前
|
Java Spring
巧用switch-case消除条件判断
`shigen`是一位致力于撰写博客文章的作者,通过记录成长历程、分享见解并留住感动瞬间。在其文章中,`shigen`介绍了多种消除if-else代码的方法,包括使用HashMap、枚举以及switch-case。最新示例展示了如何通过简洁的switch-case语句处理不同类型的请求,代码优雅且直观。此外,还预告了下一章节将探讨如何利用Spring框架的IOC能力来进一步优化条件判断。与`shigen`一起探索编程世界的每一天都充满新意!**个人IP:shigen**
40 0
巧用switch-case消除条件判断
|
2月前
|
前端开发 编译器
为什么switch里的case没有break不行
为什么switch里的case没有break不行
|
7月前
|
编译器 C语言
learn_C_deep_7 (switch 语句的基本理解、case 的作用、break的作用switch、case 推荐规则)
learn_C_deep_7 (switch 语句的基本理解、case 的作用、break的作用switch、case 推荐规则)
if-else if与switch的区别
if-else if与switch的区别
189 0
switch case 执行
switch case 执行
142 0
封装一个RxCondition,告别if else和switch case
封装一个RxCondition,告别if else和switch case
127 0
switch-case和if-else的效率比较·必看(下)
switch-case和if-else的效率比较·必看(下)
213 0
switch-case和if-else的效率比较·必看(下)
|
Java 程序员 C#
switch-case和if-else的效率比较·必看(上)
switch-case和if-else的效率比较·必看(上)
479 0
switch-case和if-else的效率比较·必看(上)
|
Java 容器 设计模式
如何优化代码中大量的if/else,switch/case?
前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单的定义一个表示状态的枚举。
2387 0
|
Java C语言
switch表达式、case穿透
记得第一次学switch的时候那是还是学习c语言的时候,整体的写if-else,switch,现在回想起来已经是很多年前的事了,好了今天让我们再来回顾下简单的switch 语法 switch(n) { case 1: 执行代码块 1 break; case 2: 执行代码块 2 b...
1877 0