序言
现在有这样一个场景:
作业结项时需要对每个 Label
中的数据完整性进行校验。你可以将 Label
理解为不同的标签,每个标签中都需要完成特定的内容,一个作业可以配置多个 Label
。
看到这个需求,我们二话不说,直接在作业结项接口中新增代码,对每个 Label
进行校验操作。
这样有问题吗?当然没问题!(你看着被代码塞满的类心虚的说到)
一顿操作猛如虎,代码 Review
骂成狗 ~
作为一名优雅(自认为)的程序员,绝不允许这种情况出现!
这时候我们就需要考虑使用设计模式来应对了。
使用了设计模式,我们可以做到在结项方法中只新增一行代码即可实现需求(歪嘴战神)~
怎么选择设计模式
使用设计模式的难点往往在于如何选用,因为只有选对了方法才能事半功倍。
这里我们对每个 Label
都有不同的处理,所以我们需要为每个 Label
都需要新建一个 handler
,将每个 Label
的校验操作解耦和隔离,使它们互不影响,将来要对其中某个 Label
修改时,也可以做尽量少的操作。
同时我们可以将所有 Label
的校验操作串成一条执行链条,只要某个节点校验到不满足条件即可提前退出,而不需要再执行后续操作。对于满足条件的节点则进入下一个节点继续校验。直到所有 Label
校验完毕。
都说到这个份上了,想必大家也知道什么设计模式适合实现这个需求了,没错,就是简单工厂模式和责任链模式。
本文不对上述两个模式做详细介绍,直接上代码。
简单工厂
我们需要对不同的 Label
获取不同的 handler
处理类,一般我们可以通过策略模式 + 简单工厂的方式来根据 Label
标签类型来创建 handler
处理类。
这里我通过将所有 handler
实现类交由 spring
容器管理,再根据需要通过 Label
类型获取,而不必每次获取都去创建。
定义所有处理类的超类
首先我们需要定义一个被所有 Label
继承的超类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public abstract class OperationResultHandler {
protected OperationResultHandler handler;
public void setSuperior(OperationResultHandler handler) { this.handler = handler; }
public abstract LabelEnum getType();
public abstract Pair<Boolean, LabelEnum> handler(DutyRequest request); }
|
定义各个Label的实现类
由于 Label
数量比较多,这里只列举一个实现类,所有实现类除具体校验逻辑外均一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Component @RequiredArgsConstructor public class OrganizeVerifyHandler extends OperationResultHandler {
private final QuestionnaireMapper questionnaireMapper;
@Override public LabelEnum getType() { return LabelEnum.ZZDY; }
@Override public Pair<Boolean, LabelEnum> handler(DutyRequest request) { if (request.getServiceIds().contains(getType().getCode())) { Integer count = questionnaireMapper.selectCount(new QueryWrapper<Questionnaire>() .eq("operation_id", request.getOperationId()) .eq("object_id", getType().getCode()) .eq("finished", false)); if (count > 0) return new Pair<>(Boolean.FALSE, getType()); } if (handler != null) return handler.handler(request); else return new Pair<>(Boolean.TRUE, getType()); } }
|
Spring容器管理
我们可以将所有 Label
的实现类交给 Spring
容器管理, 包括生产 handler
的工厂,需要使用时再通过 getHandler()
方法传入入参 Label
的类型获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Component public class OperationResultHandlerFactory implements InitializingBean, ApplicationContextAware {
private static ApplicationContext applicationContext;
private static final Map<LabelEnum, Supplier<OperationResultHandler>> BEAN_MAP = new HashMap<>();
public static OperationResultHandler getHandler(LabelEnum type) { Supplier<OperationResultHandler> supplier = BEAN_MAP.get(type); return Objects.isNull(supplier) ? null : supplier.get(); }
@Override public void afterPropertiesSet() { applicationContext.getBeansOfType(OperationResultHandler.class).values() .forEach(bean -> BEAN_MAP.put(bean.getType(), () -> bean)); }
@Override public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { OperationResultHandlerFactory.applicationContext = applicationContext; } }
|
责任链
各节点公共入参
定义每个 Label 实现类需要用的公共参数,随着责任链一直传递:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data public class DutyRequest {
private String operationId;
private Set<Long> serviceIds;
public DutyRequest(String operationId, Set<Long> serviceIds) { this.operationId = operationId; this.serviceIds = serviceIds; } }
|
构建责任链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| @Component @RequiredArgsConstructor public class OperationAbilityHandler {
private final CoOperationMapper coOperationMapper;
public void abilityChainVerify(String operation){ CoOperation coOperation = coOperationMapper.selectById(operation); if (Objects.isNull(coOperation)) throw new ServiceException("作业不存在"); Set<Long> serviceIds = StrUtil.split(coOperation.getServiceContent(), StrUtil.COMMA).stream().map(Long::parseLong).collect(Collectors.toSet()); serviceIds.add(LabelEnum.XTDY.getCode()); DutyRequest request = new DutyRequest(operation, serviceIds);
List<LabelEnum> labels = Arrays.stream(LabelEnum.values()).filter(l -> l.getPCode() != 0L).collect(Collectors.toList());
OperationResultHandler head = null; OperationResultHandler currentHandler = null;
for (LabelEnum label : labels) { OperationResultHandler handler = OperationResultHandlerFactory.getHandler(label);
if (handler != null) { if (currentHandler == null) { head = handler; } else { currentHandler.setSuperior(handler); } currentHandler = handler; } }
assert head != null; Pair<Boolean, LabelEnum> pair = head.handler(request); if (!pair.getKey()) throw new ServiceException(String.format("作业结果校验不通过,[%s]功能未完成", pair.getValue().getName())); } }
|
注入检测代码
然后我们只需要在原方法中合适位置引入一行代码即可完成作业的完整性校验:
1
| operationAbilityHandler.abilityChainVerify(operation.getOperationId());
|
总结
通过使用设计模式,我们将各个模块的耦合度降到最低,有利于后续的维护和迭代工作。