先来一张镇楼图感觉一下 if else 的魔法吧。java
有个场景,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 年后另外一个可怜的小伙伴就看到这坨东西。函数
回想上面的场景,实际上就是要根据表名去肯定 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; }); }); } }
这样可使代码在逻辑表达上会更清晰,以下:对象
if (condition) { // do something } else { return xxx; }
按照逆向思惟来,优化以下:
if (!condition) { return xxx; } // do something
还有一种常见的傻瓜编程(若有冒犯,敬请见谅,对码不对人🙏 ):
if(a > 0) { return true; } else { return false; }
话很少说了,直接 return a > 0;
不香吗?
简单来讲就是根据不一样的参数执行不一样的业务逻辑。
以下:
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();
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();
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);
就是上面的表驱动编程方法。
欢迎访问 我的博客 获取更多知识分享。