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

switch ( type ) {
case case1:
...
...
break;
case case2:
...
...
break;
case case3:
...
...
break
default:
return null;
}

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

switch ( type ) {
case case1:
return case1Func();
case case2:
return case2Func();
case case3:
return case3Func();
default:
return null;
}
即使这样也是面向过程思维的写法,以前写 C 程序的时候也总喜欢这样写,毫无设计模式可言。不仅违背开闭原则,而且随着 switch-case 分支的增多,该段代码只会越来越冗长。其实这种代码已经有成熟的模式去消除诸多的 if-else/switch-case 分支。本文就教大家在 Spring 中如何用注解+策略模式+简单工厂的方式消除 if-else/switch-case 。我们就拿 QQ 空间的个人中心举例子,假如 QQ 空间个人中心有四个 tab 分别是列出我的说说、我的日志、我的照片和我的访客。一般的后台代码很有可能如下:
 
//各个 tab 名称的枚举:
public enum UserRelatedType {
/**
* 说说
*/
SHUOSHUO("说说"), /**
* 日志
*/
RIZHI("日志"), /**
* 发布
*/
ZHAOPIAN("照片"), /**
* 访客
*/
FANGKE(""); private String desc; UserRelatedType(String desc) {
this.desc = desc;
} public String getDesc() {
return desc;
} public void setDesc(String desc) {
this.desc = desc;
}
}

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

public List<UserRelatedVO> listRelated(UserRelatedQuery query){

    UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) );
switch ( relatedType ) {
case SHUOSHUO:
return listRelatedShuoshuo( query );
case RIZHI:
return listRelatedRizhi( query );
case ZHAOPIAN:
return listRelatedZhaopian( query );
case FANGKE:
return listRelatedFangke( query );
default:
return null;
}
}

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

  • 1、定义一个注解,用来完全消除 if-else:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RelatedTypeAnnotation {
/**
* 用户相关类型名称
*/
UserRelatedType value();
}
  • 2、先定义了个接口,所有 tab 都要实现该接口。其中 list 是 tab 数据展示的方法。
public interface UserRelated {

    /**
* 列出详细信息
*
* @param query
* @return
*/
List<UserRelatedVO> list(UserRelatedQuery query);
}
  • 3、定义具体的各个 tab 的实现,继承 UserRelated 策略接口

我的说说

@Component("userRelatedShuoshuo")
@RelatedTypeAnnotation( value = UserRelatedType.SHUOSHUO )
public class UserRelatedShuoshuo implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的说说!");
return list;
}
}

我的日志

@Component("userRelatedRizhi")
@RelatedTypeAnnotation( value = UserRelatedType.RIZHI )
public class UserRelatedRizhi implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的日志!");
return list;
}
}

我的照片

@Component("userRelatedZhaopian")
@RelatedTypeAnnotation( value = UserRelatedType.ZHAOPIAN )
public class UserRelatedZhaopian implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的照片!");
return list;
}
}

我的访客

@Component("userRelatedFangke")
@RelatedTypeAnnotation( value = UserRelatedType.FANGKE )
public class UserRelatedFangke implements UserRelated {
@Override
public List<UserRelatedVO> list(UserRelatedQuery query) {
System.out.println("我的访客!");
return list;
}
}
  • 3、定义一个从 Spring context 获取 bean 的工具类
@Component
public class SpringContextUtil implements ApplicationContextAware { private ApplicationContext context; public ApplicationContext getContext() {
return context;
} @Override
public void setApplicationContext(ApplicationContext context)throws BeansException {
this.context = context;
}
}
  • 4、定义一个简单工厂,用来生产各种 tab 对象。
@Component
public class UserRelatedFactory { @Autowired
SpringContextUtil springContextUtil; private static Map<UserRelatedType, UserRelated> userRelatedMap = Maps.newConcurrentMap(); //工厂将 Spring 装配的相关的 Bean 用 Map 保存起来
public UserRelatedFactory(){
Map<String, Object> beanMap = springContextUtil.getContext().getBeansWithAnnotation(RelatedTypeAnnotation.class); for(Object userRelated : beanMap.values()) {
RelatedTypeAnnotation annotation = userRelated.getClass().getAnnotation(RelatedTypeAnnotation.class);
userRelatedMap.put(annotation.value(), (UserRelated)userRelated);
}
} public static UserRelated createRelated(UserRelatedType relatedType) {
return userRelatedMap.get( relatedType );
}
}

5、调用的代码(listRelated 会在 controller 中被调用)。

public List<UserRelatedVO> listRelated(UserRelatedQuery query){

    UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) );
UserRelated related = UserRelatedFactory.createRelated( relatedType );
if( related != null ) {
return related.list( query );
} else {
return null;
}
}

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

作者:水目沾
链接:https://juejin.im/post/5ca9f113e51d452b5e458ec3
来源:掘金

【转】消除代码中的 if-else/switch-case的更多相关文章

  1. Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句)

    Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句) 1.冗余的if/else或switch ​ 有没有朋友写过以下的代码结构,大量的if/esle判断,来选择 ...

  2. 使用Java8中的Optional类来消除代码中的null检查

    简介 Optional类是Java 8新增的一个类,Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException). —— 每个 Java 程序员都非常了解的异常 ...

  3. java中的分支结构 switch case的使用

    switch(A),括号中A的取值只能是整型或者可以转换为整型的数值类型,比如byte.short.int.char.string(jdk1.7后加入)还有枚举:需要强调的是:long是不能用在swi ...

  4. Dreamweaver中清除php代码中多余空行的方法

    使用DW自带的搜索功能,利用正则表达式 使用正则表达式搜索:\r\n\s*\r\n即可搜到代码中的空行,再用回车符\n替换即可消除代码中的多余空行

  5. 为什么说在使用多条件判断时switch case语句比if语句效率高?

    在学习JavaScript中的if控制语句和switch控制语句的时候,提到了使用多条件判断时switch case语句比if语句效率高,但是身为小白的我并没有在代码中看出有什么不同.去度娘找了半个小 ...

  6. C# 利用键值对取代Switch...Case语句

    swich....case 条件分支多了之后,会严重的破坏程序的美观性. 比如这个 上述代码是用于两个进程之间通信的代码,由于通信的枚举特别的多,所以case的分支特别的多.导致了代码的可读性,可维护 ...

  7. 知识扩展--if...else...与switch...case...的执行原理

    一.简述 编程语言中的条件分支结构有两种:if-else和switch-case,这两种条件分支之间可以相互转换,但是也存在一些区别,那么什么时候该用if-else,什么时候该用switch-case ...

  8. switch case 与 if

    case 在编程中偶尔使用到switch case语句,对于case语句的处理,出现了两种错误,现总结如下: case后必须是常量.布尔类型.字符(不能是字符串): case后如果是‘||’或者‘&a ...

  9. Java代码消除switch/case,if/else语句的几种实现方式

    转自:https://my.oschina.net/stefanzhlg/blog/372413 我们在平时的编码中,我们经常会遇到这样的情况: 使用过多的switch/case 或者 if else ...

随机推荐

  1. python 二分查找法

    @source_data:数据集 @binary_num:要查找的数 @mid:中间数的键值 def binary_search(source_data,search_num): #传入数据集计算中间 ...

  2. TCP/IP(八)之总结TCP/IP四层模型

    阅读目录(Content) 一.TCP/IP参考模型概述 1.1.TCP/IP参考模型的层次结构 二.TCP/IP四层功能概述 2.1.主机到网络层 2.2.网络互连层 2.3.传输层 2.3.应用层 ...

  3. 【react】利用shouldComponentUpdate钩子函数优化react性能以及引入immutable库的必要性

    凡是参阅过react官方英文文档的童鞋大体上都能知道对于一个组件来说,其state的改变(调用this.setState()方法)以及从父组件接受的props发生变化时,会导致组件重渲染,正所谓&qu ...

  4. 【高速接口-RapidIO】6、Xilinx RapidIO核仿真与包时序分析

    提示:本文的所有图片如果不清晰,请在浏览器的新建标签中打开或保存到本地打开 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:Vivado2015.4.2 ...

  5. JavaScript 基础排序的实现(二)

    继上一篇O(n^2)的排序算法后,这一篇主要记录O(n*logn)的排序算法 1.快排(快速排序) 这一算法的核心思想为,先随机选一个数作为标兵或者说是标记(这个数一般来说选择该无序数组的中间那个元素 ...

  6. 吴恩达机器学习笔记39-误差分析与类偏斜的误差度量(Error Analysis and Error Metrics for Skewed Classes)

    如果你准备研究机器学习的东西,或者构造机器学习应用程序,最好的实践方法不是建立一个非常复杂的系统,拥有多么复杂的变量:而是构建一个简单的算法,这样你可以很快地实现它. 构建一个学习算法的推荐方法为:1 ...

  7. soul开源网关项目搭建学习

    1. soul开源网关项目搭建学习 1.1. 地址 https://gitee.com/shuaiqiyu/soul 1.2. 介绍 官方介绍:这是一个异步的,高性能的,跨语言的,响应式的API网关. ...

  8. Redis 设计与实现 (一)--数据结构

    底层数据结构:动态字符串.字典.整数集合.双端链表.压缩列表 字符串对象: int    浮点数值 raw  字符串值>32字节 embstr   字符串值<32字节 字符串编码转换: i ...

  9. PHP中的自动加载

      自动加载? 或许你已经对自动加载有所了解.简单描述一下:自动加载就是我们在new一个class的时候,不需要手动去写require来导入这个class.php文件,程序自动帮我们加载导入进来.这是 ...

  10. Mysql实现null值排在最前或最后

    最近在做项目迁移,Oracle版本的迁到Mysql版本,遇到有些oracle的函数,mysql并没有,所以就只好想自定义函数或者找到替换函数的方法进行改造. oracle做数据排序的时候,有时候可以用 ...