业务场景中,当不同的意愿任务结束后,会有不同的动作,比如加密、解密、签署等。这些操作都是基于任务的类型进行区分,可以使用策略模式对代码进行优化,减少代码中的if…else…从而达到“对修改关闭,对扩展开放”的目的。后续再新增任务类型时,不会影响到其他任务类型的处理逻辑,减少测试回归的范围。
介绍
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
如何实现一个策略模式
还是以意愿任务结束后,进行不同的动作举例。
定义任务类型枚举
public enum WillTaskTypeEnum {
ENCRYPT_WILL("encryptWill", "加密场景"),
DECRYPT_WILL("decryptWill", "解密场景"),
SIGN_WILL("signWill", "签署场景"),;
private String code;
private String desc;
private WillTaskTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
private static final Map<String, WillTaskTypeEnum> codeMap = new HashMap<>();
static {
for (WillTaskTypeEnum willTaskTypeEnum : WillTaskTypeEnum.values()) {
codeMap.put(willTaskTypeEnum.getCode(), willTaskTypeEnum);
}
}
public static WillTaskTypeEnum fromCode(String code) {
return codeMap.get(code);
}
public String getCode() {
return this.code;
}
}
定义抽象的策略类
策略类可以是接口,可以是抽象类。如果各个算法有通用的逻辑,可以使用抽象类加模板方法进行优化,模板方法就不展开来讲了。
public abstract class AbstractTaskStrategy {
public abstract void execute(Object obj);
}
定义策略容器类,用于存放和获取策略
public class StrategyContext {
private static final Map<String, AbstractTaskStrategy>
strategyByWillTaskTypeMap = new HashMap<>();
public static void addTaskStrategy(
WillTaskTypeEnum willTaskTypeEnum, AbstractTaskStrategy strategy) {
if (strategyByWillTaskTypeMap.containsKey(willTaskTypeEnum.getCode())) {
throw new RuntimeException(willTaskTypeEnum + "策略重复");
}
strategyByWillTaskTypeMap.put(willTaskTypeEnum.getCode(), strategy);
}
public static AbstractTaskStrategy getTaskStrategy(
String willTaskType) {
if (StringUtils.isEmpty(willTaskType)) {
return null;
}
return strategyByWillTaskTypeMap.get(willTaskType);
}
}
定义策略实现类,并存放到策略容器中
目前项目基本都是使用Spring容器管理bean的创建,所以我们可以结合Spring Bean的生命周期,在bean创建之后,将策略类的bean放入到容器中。可以使用以下方法
步骤1. 将策略类跟任务类型关联
可以使用自定义注解表示可以处理的任务类型,或者在抽象类中自定义抽象方法,由子类实现表明自己可以处理哪种类型任务。此处我们使用自定义注解方式,定义自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ProcessTask {
WillTaskTypeEnum willTaskTypeEnum();
}
步骤2. 实现策略类
加密任务:
@ProcessTask(willTaskTypeEnum = WillTaskTypeEnum.DECRYPT_WILL)
@Component
public class DecryptTaskStrategyImpl extends AbstractTaskStrategy {
public void execute(Object obj) {
// TODO something....
}
}
解密任务:
@ProcesshTask(willTaskTypeEnum = WillTaskTypeEnum.ENCRYPT_WILL)
@Component
public class EncryptTaskStrategyImpl extends AbstractTaskStrategy {
public void execute(Object obj) {
// TODO something....
}
}
步骤3. 将策略实现类存入策略容器中
@Component
public class WillTaskBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 在bean初始化完成之后放到策略容器中
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof AbstractTaskStrategy)) {
return bean;
}
ProcesshTask annotation = bean.getClass().getAnnotation(ProcesshTask.class);
if (Objects.isNull(annotation)) {
return bean;
}
StrategyContext.addTaskStrategy(annotation.willTaskTypeEnum(), (AbstractTaskStrategy) bean);
return bean;
}
}
使用
@Component
public class Test {
public void finishWill(WillTaskTypeEnum willTypeEnum, Object obj) {
// 业务逻辑
doBusiness(obj);
// 调用策略
afterWillSuccess(willTypeEnum, obj);
}
void doBusiness(Object obj) {
}
public void afterWillSuccess(WillTaskTypeEnum willTypeEnum, Object obj) {
AbstractTaskStrategy abstractTaskStrategy = StrategyContext.get(willTypeEnum.getCode());
if (Objects.isNull(abstractTaskStrategy)) {
return;
}
abstractTaskStrategy.execute(obj);
}
}
优点
后续再新增其他的任务类型时,业务主流程不会再改动,只需要新增新的策略即可。从而达到“对修改关闭,对扩展开放”的目的。
缺点
业务开发场景下,业务可能会经常变动,使用策略类之前要对业务有很好的抽象,明确当前策略要做什么事,入参和返回值一定要明确。否则某个业务需求变动,当前策略无法满足,入参或返回需要新增时,需要将所有策略实现类进行修改,代价可能比简单的if…else…更高。