在责任链模式中可以定义多个处理节点(Handler
),当接收到客户端请求之后,该请求会依次经过每个处理节点,直到某个节点终止将它传递,或者所有节点都处理完为止。
这样设计的优点在于可以有效避免请求发送者与请求接收者之间的耦合关系,符合开闭原则,提高代码的扩展性。
角色 | 作用 |
---|---|
Handler | 定义一个处理请求的接口,主要包含抽象处理方法和后续Handler |
Concrete Handler | Handler的具体实现者,主要用于负责业务逻辑处理,也可以控制对后继Handler的传递行为 |
以下是一种按照责任链模式处理方式。
public abstract class Handler {
private Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public Handler getSuccessor() {
return successor;
}
public abstract void handleRequest(String msg);
}
public class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(String msg) {
if (Objects.nonNull(msg) && !msg.isEmpty()) {
System.out.println("ConcreteHandlerA pass");
if (getSuccessor() != null) {
getSuccessor().handleRequest(msg);
}
} else {
System.out.println("msg 不能为空!");
}
}
}
public class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(String msg) {
if (msg.length() <= 16) {
System.out.println("ConcreteHandlerB pass");
if (getSuccessor() != null) {
getSuccessor().handleRequest(msg);
}
} else {
System.out.println("msg长度不能超过16!");
}
}
}
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandlerA();
Handler handler2 = new ConcreteHandlerB();
handler1.setSuccessor(handler2);
handler1.handleRequest("text");
}
}
很明显,上面的这种代码实现方式实在是不够优雅,每个具体的处理器类还得要处理对下一个处理器的调用,Client
也必须得熟悉每个处理器类之间的调用关系、顺序等,这些条件都很容易导致代码出现BUG
。
我们可以改成下面这种实现方式,是让每个处理器都能处理到请求,且能自己进行判定是否需要处理,不存在被某个处理器终止而不继续向后传递的情况。
public interface IHandler {
void handleRequest(FilterRequestDTO filterRequestDTO);
default boolean accept(FilterRequestDTO filterRequestDTO) {
return true;
}
}
public class HandlerChain {
private List<IHandler> handlers = new ArrayList<>();
public void addHandler(IHandler handler) {
this.handlers.add(handler);
}
public void handle(FilterRequestDTO filterRequestDTO) {
for (IHandler handler : handlers) {
if (!handler.accept(filterRequestDTO)) {
// handler不处理
continue;
}
handler.handleRequest(filterRequestDTO);
}
}
}
public class ConcreteHandler1 implements IHandler {
@Override
public void handleRequest(FilterRequestDTO filterRequestDTO) {
System.out.println("ConcreteHandler1 handleRequest");
}
@Override
public boolean accept(FilterRequestDTO filterRequestDTO) {
String msg = filterRequestDTO.getMsg();
return Objects.nonNull(msg) && !msg.isEmpty();
}
}
public class ConcreteHandler2 implements IHandler {
@Override
public void handleRequest(FilterRequestDTO filterRequestDTO) {
System.out.println("ConcreteHandler2 handleRequest");
}
@Override
public boolean accept(FilterRequestDTO filterRequestDTO) {
String msg = filterRequestDTO.getMsg();
return msg.length() >= 16;
}
}
public class Client {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new ConcreteHandler1());
chain.addHandler(new ConcreteHandler2());
FilterRequestDTO filterRequestDTO = new FilterRequestDTO();
filterRequestDTO.setMsg("hello HandlerChain");
chain.handle(filterRequestDTO);
}
}
除此之外,我们还可以将continue
改为break
即可变为可终止责任链向下传递行为的方式。
public class HandlerChain {
private List<IHandler> handlers = new ArrayList<>();
public void addHandler(IHandler handler) {
this.handlers.add(handler);
}
public void handle(FilterRequestDTO filterRequestDTO) {
for (IHandler handler : handlers) {
if (!handler.accept(filterRequestDTO)) {
// handler不处理
break;
}
handler.handleRequest(filterRequestDTO);
}
}
}
Java Servlet
规范中定义的Filter
组件,就是一个责任链模式的实际运用场景,Filter
职责可以包含鉴权、参数校验、限流、写日志等。
public interface Filter {
default boolean accept(FilterRequestDTO filterRequestDTO) {
return true;
}
void doFilter(FilterRequestDTO filterRequestDTO, FilterChain filterChain);
}
public interface FilterChain {
void doFilter(FilterRequestDTO filterRequestDTO);
}
public class BasePriceFilter implements Filter {
@Override
public boolean accept(FilterRequestDTO filterRequestDTO) {
return true;
}
@Override
public void doFilter(FilterRequestDTO filterRequestDTO, FilterChain filterChain) {
if (accept(filterRequestDTO)) {
System.out.println("base price 业务逻辑处理");
}
filterChain.doFilter(filterRequestDTO);
}
public static BasePriceFilter create() {
return new BasePriceFilter();
}
}
public class DiscountPriceFilter implements Filter {
@Override
public boolean accept(FilterRequestDTO filterRequestDTO) {
return true;
}
@Override
public void doFilter(FilterRequestDTO filterRequestDTO, FilterChain filterChain) {
if (accept(filterRequestDTO)) {
System.out.println("discount price 业务逻辑处理");
}
filterChain.doFilter(filterRequestDTO);
}
public static DiscountPriceFilter create() {
return new DiscountPriceFilter();
}
}
public final class PriceFilterChain implements FilterChain {
private List<Filter> filters;
private int filterSize;
private int pos = 0;
@Override
public void doFilter(FilterRequestDTO filterRequestDTO) {
if (pos < filterSize) {
Filter filter = filters.get(pos++);
filter.doFilter(filterRequestDTO, this);
} else {
System.out.println("处理完了");
}
}
public PriceFilterChain(List<Filter> filters) {
this.filters = filters;
this.filterSize = filters.size();
}
}
public class FilterChainManager {
private FilterChain filterChain;
private void init() {
List<Filter> filters = new ArrayList<>();
filters.add(BasePriceFilter.create());
filters.add(DiscountPriceFilter.create());
this.filterChain = new PriceFilterChain(filters);
}
public void process(FilterRequestDTO filterRequestDTO) {
filterChain.doFilter(filterRequestDTO);
}
public static void main(String[] args) {
FilterChainManager filterChainManager = new FilterChainManager();
filterChainManager.init();
filterChainManager.process(new FilterRequestDTO());
}
}
商品报价
通常一个商品在整个报价链路中会涉及到多种价格的计算,包括:基础价格、商家报价、平台报价、折扣价等,这其中每一种价格都有自己的计算逻辑,且有些价格是需要在前一个价格的基础上叠加计算,最终得出消费者到手价,所以可以运用责任链模式来处理。
Spring Interceptor
除了前面提到的Servlet Filter
之外,类似的还有Spring Interceptor
。
HandlerInterceptor
相当于Handler
.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
HandlerExecutionChain
接口相当于HandlerChain
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
private int interceptorIndex = -1;
public void addInterceptor(HandlerInterceptor interceptor) {
this.interceptorList.add(interceptor);
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
对比直接用分支条件语句
下面这个小案例也可以用来简单说明。
public class Main {
public static void main(String[] args) {
String msg = "hello HandlerChain";
if (Objects.nonNull(msg) && !msg.isEmpty()) {
System.out.println("chain 1");
}
if (msg.length() >= 16) {
System.out.println("chain 2");
}
}
}
如果像上面这样直接用if
语句,代码看起来明显更加简洁直观,反观责任链模式将每个条件分支的处理逻辑封装在独立的处理节点中,反而增加了系统的复杂性和理解难度,特别是当链路比较长或者处理逻辑比较复杂时。
因此,是否使用责任链模式取决于具体的情况和需求。如果只需要处理少量的条件分支,并且逻辑也比较简单,则使用if
语句可能更加合适。
当然,运用设计模式主要是为了解决代码的复用性、扩展性等问题,虽然直接使用if
方式看起来简单了很多,但却不能满足开闭原则,更重要的是不能将其运用在框架中让使用者可以直接进行扩展。
最后,再来聊聊关于设计模式乱用的问题,主要突出为以下两个阶段: