如何解决代码中过多的 if else ?


先来一张镇楼图感觉一下 if else 的魔法吧。java

image.png

1、由一个几百行 if 引起的思考

有个场景,50张字典表,须要为其余服务提供一个统一的接口来校验用户输入的字典表 id 是否合法。编程

校验逻辑已经很清晰了,根据参数选择对应的表校验 id 是否存在。app

if("table_a".equals(table)) {
      // check id
    }
    if("table_b".equals(table)) {
      // check id
    }
    if("table_c".equals(table)) {
      // check id
    }...

再加上参数校验,函数调用,@Autowired bean 等等,一坨几百行的代码 ok 了。再新加表再加 if else 就好了,😋 完美。ide

如此,N 年后另外一个可怜的小伙伴就看到这坨东西。函数

2、KO 这些 if else

回想上面的场景,实际上就是要根据表名去肯定 id 是否存在表中,那么只要将表名与操做对应起来就好了。故而采用哈希表的形式,将表名与操做对应起来。部分代码以下:优化

// 用于保存表与 Function 的对应关系 
 private final Map<String, Function<Object, Object>> actionMappings = new ConcurrentHashMap<>(50);

 @PostConstruct
 private void init() {
    // map 初始化
    actionMappings.put(TableConstants.TABLE_A, (params) -> tableAManager.getById(params));
  }

/**
 * 校验逻辑
 *
 *@param table
 *@param id
 */
 public boolean valid(String table, Long id) {
    Object object = actionMappings.get(table).apply(id);
    // 不存在则校验失败
    return !Objects.isNull(object);
 }

如此,N 多行 if 被消除了,这种编程方式也叫作表驱动。虽然 if 没有了,可是在初始化 actionMappings 的时候仍是不少行重复代码。下面采用注解方式解决:this

/**
 * 标记此注解的 bean 会加入基础数据校验全局 Function Map
 *
 * @author aysaml
 * @date 2020/5/7
 */
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidHandler {

  TABLE_ENUM value();
}

value 是表名枚举,在须要的类上面加上此注解便可。同时定义一个 context 用来专门存储 actionMappings 。spa

/**
 * 数据校验上下文对象,用于保存各表的 Function Map
 *
 * @author aysaml
 * @date 2020/5/7
 */
@Component
public class CommonDataValidContext {

  private static final Logger LOGGER = LoggerFactory.getLogger(CommonDataValidContext.class);

  private final Map<String, Function<Object, Object>> actionMappings = new ConcurrentHashMap<>(50);

  /**
   * 方法加入 mappings
   *
   * @param model 表名
   * @param action 方法
   */
  public void putAction(String model, Function<Object, Object> action) {
    if (!Objects.isNull(action)) {
      actionMappings.put(model, action);
      LOGGER.info(
          "[{}] add to CommonDataValidContext actionMappings, actionMappings size : {}",
          model,
          actionMappings.size());
    }
  }

  /**
   * 执行方法获取返回结果
   *
   * @param model
   * @param param
   * @return
   */
  public <P, R> R apply(String model, P param) {
    if (actionMappings.containsKey(model)) {
      return (R) actionMappings.get(model).apply(param);
    } else {
      LOGGER.error("执行数据校验时model={}不存在!", model);
      throw new RuntimeException("基础数据校验时发生错误:" + model + "表不存在!");
    }
  }

  /**
   * 判断 mappings 中是否含有给定 model 的处理方法
   *
   * @param model
   * @return
   */
  public boolean containsKey(String model) {
    return actionMappings.containsKey(model);
  }

  /**
   * 校验执行方法的返回值是否为空
   *
   * @param model
   * @param param
   * @param <P>
   * @return
   */
  public <P> boolean validResultIsNull(String model, P param) {
    return Objects.isNull(this.apply(model, param));
  }
}

而后经过监听器的方式,将含有 ValidHandler 注解的方法加入 actionMappings 。code

/**
 * 基础数据校验处理方法监听器
 *
 * @author aysaml
 * @date 2020/5/7
 */
@Component
public class CommonValidActionListener implements ApplicationListener<ContextRefreshedEvent> {

  @Override
  public void onApplicationEvent(ContextRefreshedEvent event) {
    Map<String, Object> beans =
        event.getApplicationContext().getBeansWithAnnotation(ValidHandler.class);
    CommonDataValidContext commonDataValidContext =
        event.getApplicationContext().getBean(CommonDataValidContext.class);
    beans.forEach(
        (name, bean) -> {
          ValidHandler validHandler = bean.getClass().getAnnotation(ValidHandler.class);
          commonDataValidContext.putAction(
              validHandler.value().code(),
              (param) -> {
                try {
                  return bean.getClass().getMethod("getById", Long.class).invoke(bean, param);
                } catch (Exception e) {
                  e.printStackTrace();
                }
                return null;
              });
        });
  }
}

3、更多消除 if else 的方法。

1. 提早return

这样可使代码在逻辑表达上会更清晰,以下:对象

if (condition) {
 // do something
} else {
  return xxx;
}

按照逆向思惟来,优化以下:

if (!condition) {
  return xxx;
} 
// do something

还有一种常见的傻瓜编程(若有冒犯,敬请见谅,对码不对人🙏 ):

if(a > 0) {
      return true;
    } else {
      return false;
    }

话很少说了,直接 return a > 0; 不香吗?

2. 策略模式

简单来讲就是根据不一样的参数执行不一样的业务逻辑。
以下:

if (status == 0) {
  // 业务逻辑处理 0
} else if (status == 1) {
  // 业务逻辑处理 1
} else if (status == 2) {
  // 业务逻辑处理 2
} else if (status == 3) {
  // 业务逻辑处理 3
}...

优化以下:

  • 多态
interface A {
  void run() throws Exception;
}

class A0 implements A {
    @Override
    void run() throws Exception {
        // 业务逻辑处理 0
    }
}

class A1 implements A {
    @Override
    void run() throws Exception {
        // 业务逻辑处理 1
    }
}
// ...

而后策略对象存放在一个 Map 中,以下:

A a = map.get(param);
a.run();

2.2 枚举

public enum Status {
    NEW(0) {
      @Override
      void run() {
        //do something  
      }
    },
    RUNNABLE(1) {
      @Override
       void run() {
         //do something  
      }
    };

    public int statusCode;

    abstract void run();

    Status(int statusCode){
        this.statusCode = statusCode;
    }
}

从新定义策略枚举

public enum Aenum {
    A_0 {
      @Override
      void run() {
        //do something  
      }
    },
    A_1 {
      @Override
       void run() {
         //do something  
      }
    };
    //...
    abstract void run();
}

经过枚举优化以后的代码以下

Aenum a = Aenum.valueOf(param);
a.run();

3. Java 8 的 Optional

Optional主要用于非空判断,是 Java 8 提供的新特性。

使用以前:

if (user == null) {
    //do action 1
} else {
    //do action2
}

若是登陆用户为空,执行action1,不然执行action 2,使用Optional优化以后,让非空校验更加优雅,间接的减小if操做

Optional<User> userOptional = Optional.ofNullable(user);
userOptional.map(action1).orElse(action2);

4. 决策表

就是上面的表驱动编程方法。

欢迎访问 我的博客 获取更多知识分享。
相关文章
相关标签/搜索