责任树模式(含测试源码)
学习设计模式主要是为了代码的可读性,可维护性,让代码易于维护,隔离不同的业务,对代码进行解耦。
同时,如果需要学习框架的源码,对于设计模式的了解是必不可少的,框架中的源码会大量使用设计模式,学习设计模式也有利于更快看懂源码,同时可以提升自己对代码的理解,让自己可以从不同角度观察代码结构,对代码进行优化,重构。
责任树模式可以优化过多的if判断,是责任链模式和策略模式的结合,属于行为行设计模式。
责任链模式
责任链模式是实现了类似“链表”结构的逐级处理,通常是一条链式结构,将不同的业务串联起来:如果当前节点能够处理任务则直接处理掉,如果无法处理则委托给责任链的下一个节点,如此往复直到有节点可以处理这个任务。模型如下:
我们可以通过责任链模式来实现对业务的逐级分发,但是每个级别的业务里面可能还会有不同的策略,里面的策略就是策略模式所擅长解决的了。
策略模式
而策略模式就擅长进行策略分发,在入口处根据标记来判断程序应该调用哪部分代码,对代码进行解耦,使得每个执行逻辑都可以独立维护。而它的模型如下:
策略模式和责任链模式的区别就是,责任链模式在每个节点都可以执行完全不同的业务,而策略模式是将同一个业务,但是有多种实现。
我个人理解是,责任链主要是对类的细分,而策略模式是对方法中的执行逻辑细分。
模板模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
在责任树中,这个模式主要是作用在根节点和子节点,也就是子类中,主要是实现策略路由的方法,而调用
的行为,则由父类来控制(比如调用子类的什么方法)。主要用于定义一个统一的规则,具体实现将会以代码展示。
责任树模式
而责任链模式是既能对业务进行逐级分发,也能对同级业务进行细分,分成不同的策略执行,它的模型如下:
责任链模式的主要类分别有两个:策略处理者 和 策略路由
而各个节点需要实现的类都不一样
- 根节点只负责实现策略路由,根据标记来将业务分发到对应的节点中。
- 子节点需要将策略处理者和策略路由都实现,因为子节点也需要处理业务,当自身节点处理不了的时候,需要将任务分发给下一个节点。
- 而叶节点由于不需要进行对任务的分发,所以只需要实现策略处理者。
而他的大概模型如下:
现在我们来先创建一个策略处理者的接口:
/**
* 策略处理者,所有节点(根节点除外)都必须实现这个接口
* @param <P> 接口入参
* @param <R> 接口返回值
* @author lin
* @date 2021/5/20 15:59
**/
public interface StrategyHandler<P,R> {
/**
* 审批
* @param param 参数
* @return 返回值
*/
R approve(P param);
}
策略处理者的实现类主要实现这个审批方法,每个实现都根据自己的业务实现不同的审批逻辑,这样就解决了同一个方法中过多的判断来确定调用哪一个审核逻辑,大大提高了代码的可读性和可维护性。
接下来是策略路由的类:
public abstract class StrategyRouter<P,R> {
/**
* 策略的映射器,根据入参来路由到指定的策略执行者
* @param <P>
* @param <R>
*/
public interface StrategyMapper<P,R>{
/**
* 根据入参获取到对应的策略执行者
* @param param 入参
* @return 具体的执行者
*/
StrategyHandler<P,R> get(P param);
}
/**
* 在类初始化时,会调用初始化类(也就是实现类)的registerStrategyMapper()方法获取到接口的实现
*/
private StrategyMapper<P,R> strategyMapper;
/**
* 类初始化时注册分发策略 Mapper
*/
@PostConstruct
private void abstractInit() {
//获取到StrategyMapper接口的实现
strategyMapper = registerStrategyMapper();
if(strategyMapper == null){
throw new NullPointerException("未找到策略分发者");
}
}
/**
* 执行审批方法
* @param param 入参
* @return 返回值
*/
public R strategyApprove(P param){
//调用实现StrategyMapper接口的匿名类的get方法,此时get方法会根据入参,返回对应的策略执行者,再调用approve方法
return strategyMapper.get(param).approve(param);
}
/**
* 分发策略的具体实现
* @return 策略映射器
*/
public abstract StrategyMapper<P,R> registerStrategyMapper();
public StrategyMapper<P,R> getStrategyMapper(){
return strategyMapper;
}
}
根节点和子节点需要实现策略路由的抽象类,并实现类中的接口,重写抽象类中的StrategyMapper接口,实现get方法。
而get方法是用于实现路由逻辑,调用get方法时,根据子类实现的get方法中的逻辑来判断是否需要本类来处理业务,还是需要路由给下面的叶节点来处理业务。
上面那两个类是两个最核心的类,而负责实现业务的类如下:
@Service
public class StrategyRootNode extends StrategyRouter<ReqEntity, ResEntity>{
@Autowired
private Strategy1 strategy1;
@Autowired
private Strategy2 strategy2;
@Override
public StrategyMapper<ReqEntity, ResEntity> registerStrategyMapper() {
return param -> {
int type=param.getType();
if(type==1){
return strategy1.getStrategyMapper().get(param);
}
if(type>1){
return strategy2;
}
return null;
};
}
}
首先先定义一个根节点,负责根据入参判断来调用哪一个业务service,如果那个业务service还有子节点,则返回那个子节点的 strategyMapper ,再调用strategyMapper里的get方法,执行子节点的路由逻辑,路由到合适的策略执行者来执行代码,如果没有子节点,则直接返回策略执行者就好了。
此时,可以调用根节点继承的 StrategyRouter 抽象类中的 strategyApprove 方法,这时,这个方法会调用 strategyMapper.get(),来返回具体的策略处理者,再调用策略处理者的某个方法,来实现业务。这样就可以实现所有调用都可以只通过根节点来完成,不需要硬编码来实现各个不同策略的分发。
以上就是本篇文章的全部内容,源代码即案例如下:https://github.com/linFeng185/TestProjects/tree/master/responsibility-tree