责任链模式

一、什么是责任链模式

避免请求的发送者与接收者之间的耦合,让多个对象都有可能接收请求,将这些对象连成一条链,并且沿着这条链传递请求,直到有对象处理他为止。即将责任抽象,实现责任接口,以链式调用的方式对请求对象进行处理。

责任链模式

责任链模式

特点

1、链上的每个对象都有机会处理请求。

2、链上的每个对象都持有下一个要处理请求对象的引用。

3、链上的某个对象无法处理当前请求时,会将相同的请求传递给下一个对象。

二、没使用责任链模式场景

小明要去上学,妈妈给小明列了一些上学前需要做的清单(洗头、吃早饭、洗脸),小明必须按照妈妈的要求,把清单上打钩的事情做完了才可以上学。

/**定义一个准备列表**/
public class PreparationList {

    /**是否洗了脸**/
    private boolean washFace;
    
    /**是否洗了头**/
    private boolean washHair;
    
    /**是否刷了牙**/
    private boolean haveBreakfast;

    /**get、set、构造方法等。。。**/

}
/**定义一个学习类**/
public class Study {
    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("我可以去上学了!");
    }
}

这个场景的弊端

我们的主流程是学习,但是把要准备做的事情这些动作耦合在学习中,这样会有两个问题:

1、PreparationList中增加一件事情的时候,比如增加化妆、打扫房间,必须修改study方法进行适配。

2、当这些事情的顺序需要发生变化的时候,必须修改study方法,比如先洗头再洗脸,那么代码顺序需要互换位置。

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

三、对于这个场景如何使用责任链模式

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 Study {
    public void study() {
        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 WashFaceFilter extends AbstractPrepareFilter {
   
    public WashFaceFilter(AbstractPrepareFilter nextPrepareFilter) {
        super(nextPrepareFilter);
    }

    @Override
    public void prepare(PreparationList preparationList) {
        if (preparationList.isWashFace()) {
            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方法,只需要修改调用方即可。但是两个明显的缺点对客户端并不友好:

1、增加、减少责任链对象,需要修改客户端代码,即比如我这边想要增加一个打扫屋子的操作,testResponsibility()方法需要改动。

2、AbstractPrepareFilter washFaceFilter = new WashFaceFilter(haveBreakfastFilter)这种调用方式不够优雅,客户端需要思考一下,到底真正调用的时候调用三个Filter中的哪个Filter。

四、升级版责任链模式

/**定义一个接口**/
public interface StudyPrepareFilter {
    public void doFilter(PreparationList preparationList, FilterChain filterChain);
}

这里有一个计数器,如果所有的StudyPrepareFilter没有调用完毕,那么调用下一个,否则执行Study的study()方法。 

/**实现一个责任链**/
public class FilterChain implements StudyPrepareFilter {

    private int pos = 0;
 
    private Study study;

    private List<StudyPrepareFilter> studyPrepareFilterList;
   
    public FilterChain(Study study) {
        this.study = study;
    }

    public void addFilter(StudyPrepareFilter studyPrepareFilter) {
        if (studyPrepareFilterList == null) {
            studyPrepareFilterList = new ArrayList<StudyPrepareFilter>();
        }
      
        studyPrepareFilterList.add(studyPrepareFilter);
    }
   
    @Override
    public void doFilter(PreparationList thingList, FilterChain filterChain) {
        // 所有过滤器执行完毕
        if (pos == studyPrepareFilterList.size()) {
            study.study();
        }
       
        studyPrepareFilterList.get(pos++).doFilter(thingList, filterChain);
    }
}
/**洗头**/
public class WashHairFilter implements StudyPrepareFilter {
    @Override
    public void doFilter(PreparationList preparationList, FilterChain filterChain) {
    if (preparationList.isWashHair()) {
        System.out.println("洗完头发");
    }
    filterChain.doFilter(preparationList, filterChain);
    }   
}
/**洗脸**/
public class WashFaceFilter implements StudyPrepareFilter {
    @Override
    public void doFilter(PreparationList preparationList, FilterChain filterChain) {
        if (preparationList.isWashFace()) {
            System.out.println("洗完脸");
        }
        filterChain.doFilter(preparationList, filterChain);
    }
}
/**吃饭**/
public class HaveBreakfastFilter implements StudyPrepareFilter {
    @Override
    public void doFilter(PreparationList preparationList, FilterChain filterChain) {
        if (preparationList.isHaveBreakfast()) {
            System.out.println("吃完早饭");
        }
        filterChain.doFilter(preparationList, filterChain);
    }
}
/**测试方法**/
@Test
public void testResponsibilityAdvance() {
    PreparationList preparationList = new PreparationList();
    preparationList.setWashFace(true);
    preparationList.setWashHair(false);
    preparationList.setHaveBreakfast(true);
       
    Study study = new Study();
       
    StudyPrepareFilter washFaceFilter = new WashFaceFilter();
    StudyPrepareFilter washHairFilter = new WashHairFilter();
    StudyPrepareFilter haveBreakfastFilter = new HaveBreakfastFilter();
      
    FilterChain filterChain = new FilterChain(study);
    filterChain.addFilter(washFaceFilter);
    filterChain.addFilter(washHairFilter);
    filterChain.addFilter(haveBreakfastFilter);
     
    filterChain.doFilter(preparationList, filterChain);
}

至此增加、修改责任对象,客户端调用代码都不需要再改动。有的人可能会问,你这个增加、减少责任对象,不是还需要修改testResponsibility()。我们回想一下,Servlet中我们增加或减少Filter需要改动什么代码吗?不用,我们需要改动的只是web.xml而已。同样的道理,FilterChain里面有studyPrepareFilterList,我们完全可以把FilterChain做成一个Spring Bean,所有的Filter具体实现类也都是Spring Bean,注入studyPrepareFilterList就好了,伪代码为:

<bean id="filterChain" class="xxx.xxx.xxx.FilterChain">
    <property name="studyPrepareFilterList">
        <list>
            <ref bean="washFaceFilter" />
            <ref bean="washHairFilter" />
            <ref bean="haveBreakfastFilter" />
        </list>
    </property>
</bean>

责任链模式的优点及使用场景

最典型的就是Servlet中的Filter,有了上面的分析,可以理解Servlet中责任链模式,为什么一个一个的Filter需要配置在web.xml中。最后说说责任链模式的优点吧,大致有以下几点:

1、实现了请求发送者与请求处理者之间的松耦合

2、可动态添加责任对象、删除责任对象、改变责任对象顺序,非常灵活

3、每个责任对象专注于做自己的事情,职责明确

什么时候需要用责任链模式?系统设计的时候,注意区分主次就好,即哪部分是核心流程,哪部分是辅助流程,辅助流程是否有N多if...if...if...的场景,如果是且每个if都有一个统一的抽象,那么抽象辅助流程,把每个if作为一个责任对象进行链式调用,优雅实现,易复用可扩展。

上一篇:drf 反序列化和保存数据


下一篇:Servlet——Filter过滤器