很多业务比如工单系统,都会遇到一个这样的业务场景,对于不同类型的工单,他们的查询思路大同小异,如果单独为每一个账单类型写一份代码,会产生至少10份流程非常相似的代码,从代码维护和简洁性上看明显存在不小的问题;而且有让开发工作成为劳动密集型的嫌疑,不符合追求的自动化/精简工作量的原则。
其实仔细梳理一下业务流程,业务的执行过程可以抽象为一定的算法骨架:
构造通用查询条件
构造特定的查询条件
查询数据库,包括历史库和实时库
去重
排序
对于算法可以抽象的流程,一般可以采用设计模式中的模版方法来解这个难题:模板方法模式是一种行为设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。在这里,「构造特定的查询条件」这一步就是暴露给各具体账单子类进实现的。详细学习模版方法可以参考 模版方法
而且通过观察发现,比如第3步查询数据库,不同的类型的工单使用的查询数据库,无非就是参数的不同罢了。对于参数不同又希望函数能适用的场景,一般是通过泛型来解决。
关于泛型,在Java和TS等现代语言中都有很好的支持和应用,以Java为例子,简单和大家分享一下模版方法+泛型的使用:
基类中:
java复制代码
public abstract class TicketRepositoryBase<I extends TicketDOCriteriaBase, T extends QueryTicketOptionBaseEntity> {
// 模版方法
protected final List<?> queryTicketTemplate(I criteria, T queryOption){
1. 构造通用查询条件(本类实现)
2. 构造特定的查询条件(子类实现)
3. 查询数据库,包括历史库和实时库(本类实现)
4. 去重(本类实现)
5. 排序(本类实现)
}
// 2. 构造特定的查询条件(子类实现)
abstract void buildQueryCriterion(I criteria, T queryOption);
}
TicketDOCriteriaBase和QueryTicketOptionBaseEntity分别是查询构造条件和查询参数实体的基类,可以放一些通用的函数。下面的RepairTicketDOCriteria, QueryRepairTicketOptionEntity都需要分别继承他们,实现多态。
某具体类型工单中(以维修工单举例):
// <RepairTicketDOCriteria, QueryRepairTicketOptionEntity> 可以理解为是把具体的类型当作参数传进去基类里,这样基类的I和T就有了具体的类型。
public class RepairTicketRepositoryImpl extends
TicketRepositoryBase<RepairTicketDOCriteria, QueryRepairTicketOptionEntity> {
// 只需要实现的函数
void buildQueryCriterion(RepairTicketDOCriteria criteria, QueryRepairTicketOptionEntity queryOption){
// 构造具体类型工单需要支持的查询条件
}
}
构造通用查询条件(本类实现)
构造特定的查询条件(子类实现)
查询数据库,包括历史库和实时库(本类实现)
去重(本类实现)
排序(本类实现)
可以看到,在这5点里,只有第二点的代码变化较多,需要延迟到子类实现。其他的相似的代码都可以通过泛型,在父类中通过子类指定具体的类型参数,以泛型+多态的方式插入模版方法参数里。