共计 4325 个字符,预计需要花费 11 分钟才能阅读完成。
先来一张镇楼图感受一下 if else 的魔法吧。
一、由一个几百行 if 引发的思考
有个场景,50 张字典表,须要为其余服务提供一个对立的接口来校验用户输出的字典表 id 是否非法。
校验逻辑曾经很清晰了,依据参数抉择对应的表校验 id 是否存在。
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 就行了,???? 完满。
如此,N 年后另一个可怜的小伙伴就看到这坨货色。
二、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 的时候还是很多行反复代码。上面采纳注解形式解决:
/** | |
* 标记此注解的 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。
/** | |
* 数据校验上下文对象,用于保留各表的 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。
/** | |
* 根底数据校验解决办法监听器 | |
* | |
* @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 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. 决策表
就是下面的表驱动编程办法。
欢送拜访集体博客 获取更多常识分享。