设计模式-责任链模式

先简单写下自己看过一些介绍之后,对它的简单理解吧:

就是在进行核心业务逻辑之前,会存在一些前置条件(或处理步骤),而如果此时我们要增加流程(或修改),就会对代码的逻辑侵入修改较大,就可以采取链式调用前面的处理步骤,把耦合降至最低?

责任链定义

责任链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止

  • 链上的每个对象都有机会处理请求
  • 链上的每个对象都持有下一个要处理对象的引用
  • 链上的某个对象无法处理当前请求,那么它会把相同的请求传给下一个对象

类比:打客服电话前面所走的流程,机器人可以解决的就直接解决,无法解决的会交给人工处理,同时如果此岗位的人工也无法处理,则会转交给其他部门的人工处理

image-20240627123419992

使用场景

  1. 当必须按顺序执行多个处理者时可以使用该模式,无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者
  2. 当程序需要使用不同方式处理不同种类请求而且请求类型和顺序预先未知时可以使用责任链模式,该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求
  3. 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序(暂时没有深刻的理解)

不采用责任链的话

此处参考1.代码范例了

在学习前面需要完成洗脸、洗头、吃早餐等等处理步骤

@Data
public class PreparationList {
    /**
     * 是否洗脸
     */
    private boolean washFace;

    /**
     * 是否洗头
     */
    private boolean washHair;

    /**
     * 是否吃早餐
     */
    private boolean haveBreakfast;
}

study类

public class Study {

    //不采取责任链的话,就只能采用 if 进行冗余判断
    
    public void study(PreparationList preparationList) {
        if (preparationList.isWashHair()) {
            System.out.println("洗脸");
        }
        if (preparationList.isWashHair()) {
            System.out.println("洗头");
        }
        if (preparationList.isHaveBreakfast()) {
            System.out.println("吃早餐");
        }

        System.out.println("我可以去上学了!");
    }
}

从上面代码就可以看出,当我们想要增加「刷牙」流程时,需要修改的代码较多,或者想要调换洗脸和洗头的顺序时,也需要调整代码判断顺序

违背开闭原则,即当我们扩展功能的时候需要去修改主流程,无法做到对修改关闭、对扩展开放

采用责任链

特点:链上的每个对象都持有下一个对象的引用

抽象出AbstractPrepareFilter类

public abstract class AbstractPrepareFilter {

    //下一个对象的引用
    private AbstractPrepareFilter nextPrepareFilter;

    public AbstractPrepareFilter(AbstractPrepareFilter nextPrepareFilter) {
        this.nextPrepareFilter = nextPrepareFilter;
    }

    public void doFilter(PreparationList preparationList, Study study) {
        //调用准备工作
        prepare(preparationList);

        //如果没有了下一个对象的引用,则调用 核心业务
        if (nextPrepareFilter == null) {
            study.study();
        } else {
            //如果还有下一个对象的引用,继续交给下一个对象处理
            nextPrepareFilter.doFilter(preparationList, study);
        }
    }

    public abstract void prepare(PreparationList preparationList);

}

实现抽象类

//洗脸的流程

public class WashFaceFilter extends AbstractPrepareFilter {

    public WashFaceFilter(AbstractPrepareFilter nextPrepareFilter) {
        //父类的构造函数
        super(nextPrepareFilter);
    }

    @Override
    public void prepare(PreparationList preparationList) {
        //处理流程
        if (preparationList.isWashFace()) {
            System.out.println("洗脸");
        }
    }
}
//洗头

public class WashHairFilter extends AbstractPrepareFilter {

    public WashHairFilter(AbstractPrepareFilter nextPrepareFilter) {
        super(nextPrepareFilter);
    }

    @Override
    public void prepare(PreparationList preparationList) {
        if (preparationList.isWashHair()) {
            System.out.println("洗头");
        }
    }
}
//吃早餐

public class HaveBreakfastFilter extends AbstractPrepareFilter {

    public HaveBreakfastFilter(AbstractPrepareFilter nextPrepareFilter) {
        super(nextPrepareFilter);
    }

    @Override
    public void prepare(PreparationList preparationList) {
        if (preparationList.isHaveBreakfast()) {
            System.out.println("吃早餐");
        }
    }
}

调用方法

@Test
public void testResponsibility() {
    PreparationList preparationList = new PreparationList();
    preparationList.setWashFace(true);
    preparationList.setWashHair(false);
    preparationList.setHaveBreakfast(true);

    Study study = new Study();

    AbstractPrepareFilter haveBreakfastFilter = new HaveBreakfastFilter(null);
    AbstractPrepareFilter washFaceFilter = new WashFaceFilter(haveBreakfastFilter);
    AbstractPrepareFilter washHairFilter = new WashHairFilter(washFaceFilter);

    washHairFilter.doFilter(preparationList, study);
}

以后再去修改流程的时候,就不需要再去修改 study 核心业务了

但是调用的时候,需要调用最开始的 Filter ,对客户不友好

升级版责任链

这里没有采用参考1.的方法,而是结合springboot自动注入的特性,进行了结合

/**
 * 处理器接口
 */
public interface Handler {
    void setNextHandler(Handler handler);
    void handleCheck(User user);
}

实现

//吃早餐

@Component
public class HaveBreakfastHandler implements Handler {
    private Handler nextHandler;

    @Override
    public void setNextHandler(Handler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleCheck(User user) {
        if(user.isBreakfast()){
            System.out.println("吃早饭了");
            
            //如果后续还有引用,继续调用
            if(nextHandler != null){
                nextHandler.handleCheck(user);
            }
        }
    }
}
@Component
public class WashHairHandler implements Handler {
    private Handler nextHandler;

    @Override
    public void setNextHandler(Handler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleCheck(User user) {
        if(user.isWashFire()){
            System.out.println("洗头了");
            
            //如果后续还有引用,继续调用
            if(nextHandler != null){
                nextHandler.handleCheck(user);
            }
        }
    }
}
@Component
public class WashFaceHandler implements Handler {
    private Handler nextHandler;

    @Override
    public void setNextHandler(Handler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleCheck(User user) {
        if(user.isWashFace()){
            System.out.println("洗脸了");
            
            //如果后续还有引用,继续调用
            if(nextHandler != null){
                nextHandler.handleCheck(user);
            }
        }
    }
}

核心代码,也进行一个实现,不过不调用后续链

@Component
public class Study implements Handler {
    private Handler nextHandler;

    @Override
    public void setNextHandler(Handler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleCheck(User user) {
        //调用核心代码
        if(nextHandler == null){
            study();
        }
    }

    private void study() {
        System.out.println("可以上学了");
    }
}

配置责任链

@Configuration
public class ChainConfiguration {

    /**
     * 配置责任链
     * @param haveBreakfastHandler 吃早餐
     * @param washFaceHandler 洗脸
     * @param washHairHandler 洗头
     * @param study 核心代码
     * @return 链开头的处理者
     */
    @Bean
    public Handler chain(HaveBreakfastHandler haveBreakfastHandler,
                         WashFaceHandler washFaceHandler,
                         WashHairHandler washHairHandler,
                         Study study) {
        //洗头 -> 洗脸 -> 吃早餐
        washHairHandler.setNextHandler(washFaceHandler);
        washFaceHandler.setNextHandler(haveBreakfastHandler);

        haveBreakfastHandler.setNextHandler(study);

        //返回最开始的处理流程
        return washHairHandler;
    }
}

调用

@SpringBootApplication
public class ChainApplication implements CommandLineRunner {

    @Autowired
    private Handler chain;

    public static void main(String[] args) {
        SpringApplication.run(ChainApplication.class, args);
    }


    @Override
    public void run(String... args) throws Exception {
        User user = new User();
        chain.handleCheck(user);
    }
}

洗头了
洗脸了
吃早饭了
可以上学了

参考

  1. https://www.cnblogs.com/xrq730/p/10633761.html#!comments
  2. https://refactoringguru.cn/design-patterns/chain-of-responsibility
  3. 代码仓库