在很多时候,我们代码中会有很多分支,而且分支下面的代码又有一些复杂的逻辑,相信很多人都喜欢用 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 个、且分支代码相似且冗长的情况下就应该考虑这种模式。这种模式写出的代码面向对象、清晰、易扩展还高大上,何乐而不为呀,赶紧试试吧!