背景
最近一个开发的朋友开始学习Java,她之前写脚本语言多一点,对于面向对象的一些设计还是不够熟练,她写一个代码特别的累,需要大量堆if-else语句,她的直觉告诉她这样写肯定不够好,然后咨询了我。
场景介绍
她的代码逻辑是要处理一个BinlogDTO的Message消息,然后做一些后续的逻辑,由于前面的设计是很多张表都会沿用这个BinlogDTO对象并且发送的一个Message的Topic,这个BinlogDTO的对象大概定义为:
class BinlogDTO {
private String tableName;
//....其他省略
}
并且MQ使用的RocketMQ消息中间件,她这边的逻辑是要根据每个表和不同的tag去处理
发现这个表非常多,而且后续可能持续不断的添加新的表,每个圆圈就代表一个变化的逻辑。所以很正常会包含大量的if-else。
思考
这个问题本质就是区分变化的纬度,这里发现表的数量是一个重要的维度,如果不加以管控,大量的判断语句无法避免,回想23种设计模式,有没有一种方式能解决这种场景,我认为这里抓住两个要点,我们处理的统一对象是一个消息,处理的逻辑是需要判断的,最好能解耦消息的接收和消息的处理,那很自然就会想到责任链模式了。
回顾下责任链模式的UML图
调用者逻辑
实践
接下来就是代码实现
定义BinlogDTO对象,也是Message封装的消息体结构
class BinlogDTO implements Serializable {
private String tableName;
//....setter getter 省略
}
定义消息处理的Handler,是一种能力的抽象,后面新加表,就是基于此能力进行扩展
interface BinlogHandler {
/**
* 处理消息的tag is null
* @param message
*/
void processIgnore(Message message);
/**
* 处理tag 不为 null
* @param message
*/
void processMergeTag(Message message);
/**
* 获取表名
* @return
*/
String getTableName();
}
定义模板抽象类,是否当前Handler处理这个逻辑是一个可以固定下来的逻辑,因此可以写个模板,包括后期各个Handler可以复用的方法也可以放在此基类里面
abstract class AbstractBinlogHandler implements BinlogHandler {
public boolean isMatch(Message message) {
return getTableName().equals(getTableName(message));
}
private String getTableName(Message message) {
// TODO 获取message 里的的table
BinlogDTO binlogDTO = SerializationUtils.deserialize(message.getBody());
return binlogDTO.getTableName();
}
}
接下来定义具体的处理Handler,真正干活,扩展的东西,后面新添加表,只需要继承AbstractBinlogHandler去实现对应的tag实现即可,当然也要表明自己的表名,与消息体里的tableName一致。
class TableAHandler extends AbstractBinlogHandler {
@Override
public void processIgnore(Message message) {
System.out.println("proccsse table a ignore tag");
}
@Override
public void processMergeTag(Message message) {
System.out.println("processe table a merge tag");
}
@Override
public String getTableName() {
return "tableA";
}
}
class TableBHandler extends AbstractBinlogHandler {
@Override
public void processIgnore(Message message) {
System.out.println("proccsse table b ignore tag");
}
@Override
public void processMergeTag(Message message) {
System.out.println("processe table b merge tag");
}
@Override
public String getTableName() {
return "tableB";
}
}
接下来定义责任链的处理,就是解耦消息的消费与处理受体的解耦:
class BinlogHandlerChain {
private static List<AbstractBinlogHandler> binlogHandlerList = new ArrayList<>();
static {
// load all handler
// 可以利用 Spring的注入机制
TableAHandler tableAHandler = new TableAHandler();
TableBHandler tableBHandler = new TableBHandler();
binlogHandlerList.add(tableAHandler);
binlogHandlerList.add(tableBHandler);
}
/**
* 这里就是任务的提交,客户端只用调这里即可,不用关心去调谁了
*/
public void doSubmit(Message message) {
// 找出对应表匹配的handler
List<AbstractBinlogHandler> handlerList = binlogHandlerList.stream().filter(e -> e.isMatch(message)).collect(Collectors.toList());
/*
分别处理tag和不需要的tag
*/
if (message.getTag() == null) {
handlerList.forEach(e -> e.processIgnore(message));
} else {
handlerList.forEach(e -> e.processMergeTag(message));
}
}
}
使用
模拟tableA的binlog带标签和不带标签使用
public static void main(String[] args) {
Message message = new Message();
/*
构造消息
*/
BinlogDTO binlogDTO = new BinlogDTO();
binlogDTO.setTableName("tableA");
message.setBody(SerializationUtils.serialize(binlogDTO));
/*
处理消息
*/
BinlogHandlerChain binlogHandlerChain = new BinlogHandlerChain();
// tag is null
binlogHandlerChain.doSubmit(message);
// tag is not null
message.setTag("tagA");
binlogHandlerChain.doSubmit(message);
}
输出:
processe table a merge tag
proccsse table a ignore tag
扩展
新来的表只用新建类
class tableX extends AbstractBinlogHandler 即可。
总结&思考
可能会发现我写的责任链跟标准的责任链有些不一样,例如标准的责任链是关注链条顺序的,我并不关心,因此不需要控制它的顺序。这就是学习设计模式的正确意义,它传递的是一种抽象思想,而不是具体实现,结合实际业务的变种才是设计模式正确的实践方式。