平常在公司看到很多业务逻辑的代码可能就是if/else各种判断,虽然也有一些设计模式能够解决一些问题,但是感觉都不是特别好用。刚好这次有个场景比较符合这个命题来聊一下如何去实践的.
先说一下场景吧:
目前有些场景需要基于BinLog的变化去做一些业务变更操作,比如有
- 一些表字段中冗余了各种名称,希望这些源表发生变更的时候,能够顺带更新冗余字段表中的值。
- 随着一些表的数据越来越大,一些表关联让SQL越来越慢,目前是希望压缩数据,所以数据发生变化了需要实时通知某些业务。
- 还有一些最终一致性的业务
目前已经将Binlog发送到消息队列存储好了,表名为Tag,开发根据自己关注的表名指定好,然后去处理特定的逻辑
消费者:
伪代码 :
public class BinlogConsumer{
public void consumer(TableInfo table){
String tableName = table.getTableName();
if(request.equals("TB_A")){
// 处理A表变更后的业务逻辑
}else if(request.equals("TB_B")){
// 处理B表变更后的逻辑
}else if(request.equals("TB_C")){
// 处理C表变更后的逻辑
}else if(request.equals("TB_D")){
// 处理D表变更后的逻辑
}
}
}
随着业务要处理的表越来越多,会发现判断也会越来越多。
如果每个变更业务逻辑的方法抽象成小的方法还好,最怕的就是有的开发拿着就是一把梭,所有逻辑全部怼到一个方法!!!
我已经遇到过太多了!太辛苦了。
目前对于这种场景的话一开始制定好规范,让其他开发按照同样的套路去做的话会省力一点。
通过JDK8的Function编程去实现:
方法1
import org.springframework.util.Assert;
import java.util.function.Consumer;
/**
* @Module 执行器
* @Description 等值比较处理
* @Author liukaixiong
* @Date 2020/12/11 15:48
*/
public interface EqualsValueInvoke<T> {
/**
* 等值比较执行方法
*
* @param eqA 值A
* @param eqB 值B
* @param req 请求参数
* @param consumer 具体的执行方法
* @return
*/
default void invoke(String eqA, String eqB, T req, Consumer<T> consumer) {
Assert.notNull(eqA, "eqA is not null");
Assert.notNull(eqB, "eqB is not null");
if (eqA.equals(eqB)) {
consumer.accept(req);
}
}
}
使用的测试用例:
public class EqualsValueInvokeTest extends TestCase implements EqualsValueInvoke<String> {
@Test
public void testInvoke() {
String request = "Table_B";
// 在这个方法内可以根据特定的表找对应的处理方法
invoke(request, "Table_A", request, this::processTableA);
invoke(request, "Table_B", request, this::processTableB);
}
public void processTableA(String req) {
String s = "processTableA_" + req;
System.out.println(s);
}
public void processTableB(String req) {
String s = "processTableA_" + req;
System.out.println(s);
}
}
看上去会稍微清晰一点,当然实际上还是if、else的写法。
方法2
在方法1的基础上在升级一下,利用注解来标明关注的TableName,Map来存储关系
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @Module 处理器
* @Description 等值处理器
* @Author liukaixiong
* @Date 2020/12/11 14:38
*/
public interface EqualsValueProcess<T> extends InitializingBean {
Map<String, Method> equalsMap = new HashMap<String, Method>();
@Override
default void afterPropertiesSet() throws Exception {
Method[] methods = this.getClass().getMethods();
Arrays.stream(methods).filter((t) -> {
return t.getAnnotation(RequestMapping.class) != null;
}).forEach((method) -> {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String value = requestMapping.value()[0];
equalsMap.put(value, method);
});
}
default void process(Object obj, String key, T request) throws Exception {
Method method = equalsMap.get(key);
if (method != null) {
method.invoke(obj, request);
} else {
System.out.println("找不到匹配的值:" + key);
}
}
}
测试用例:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Module TODO
* @Description TODO
* @Author liukaixiong
* @Date 2020/12/11 15:19
*/
@RunWith(JUnit4.class)
public class EqualsValueProcessTest implements EqualsValueProcess<String> {
// 假设这是Binlog传递过来的参数
private String defaultTable = "Table_A";
@Before
public void init() throws Exception {
afterPropertiesSet();
}
@Test
public void testProcess() throws Exception {
process(this, defaultTable, "你好");
}
@RequestMapping(value = "Table_A")
public void methodA(String param) {
System.out.println(param + "\t A");
}
@RequestMapping(value = "Table_B")
public void methodC(String param) {
System.out.println(param + "\t B");
}
}
当然这里只是做测试(用的是SpringMVC的@RequestMapping
),可以根据许多场景去制定注解的规则,比一定非得是等值比较。
这样的实现的话,就会将对应的值根据注解路由到特定的方法。
可能是使用起来比较习惯的方式。
第一种方式的话,读起来感觉会更清晰一点,所有表名旁边就是对应的处理逻辑方法,性能比第二种要好。
第二种方式的话:使用起来会更方便,直接告诉注解你需要那张表的数据,用惯了Spring的会更贴切。
其实根据这些思路还可以衍生其他方法,定义好规范的话,统一管理对大家都好。
网友评论