设计模式:责任链模式-概念、实现及netty中的责任链模式

学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)

本文先介绍了责任链模式的概念及简单实现。再贴了netty中对责任链的实现。最后总结了一点点思考。

1、概念相关

1.1、概念

责任链模式为请求创建了一个接收者对象的链,每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,沿着这条链传递请求,直到有对象处理它为止。

1.2、解决了什么:

客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

1.3、场景:

1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。

2、简单实现

2.1 代码

注:代码中getter 和 setter都省略

  1. 定义一个请求
public class Request {

    private String name;

    private int days;
    
}
  1. 定义一个返回结果
public class Result {

    private boolean agree;

    public Result(boolean agree) {
        this.agree = agree;
    }
}
  1. 定义一个处理接口
public interface Handler {
    // 每个处理器持有链,能取到链上的请求或者传递请求
    Result deal(Chain chain);
    
    interface Chain {
        // 获取请求
        Request request();
        // 传递请求
        Result proceed(Request request);
    }
}

  1. 定义一个处理接口的链
public class HandlerChain implements Handler.Chain {
    // 持有链要处理的请求
    private Request request;
    // 持有链上所有的处理器
    private Queue<Handler> handlers;

    public HandlerChain(Request request) {
        this.request = request;
    }
    // 添加一个处理器
    public HandlerChain addHandler(Handler handler) {
        if (handlers == null) {
            handlers = new LinkedList<>();
        }
        handlers.add(handler);
        return this;
    }
    // 实现接口的方法- 获取请求
    public Request request() {
        return request;
    }
    // 实现接口的方法- 处理请求
    public Result proceed(Request request) {
        // 取队首的处理器开始请求。如果队首的处理器处理了,直接返回结果
        // 如果队首的处理器选择传递请求,又会进这个proceed方法。取新的队首处理
        // 为什么是传递而不是直接遍历队列,如果result = null,继续;result != null,返回呢? 因为传递的方式,可以对request再次处理。A处理器可以做简单处理,再传递给B。
        Handler handler = handlers.poll();
        return handler.deal(this);
    }
}
  1. 定义三个处理者
public class AHandler implements Handler{
    public Result deal(Chain chain) {
        Request request = chain.request();
        // 只处理小于等于1的请求,大于1的请求被传递了
        if (request.getDays() > 1) {
            // 这里可以对request做部分处理,再传递
            return chain.proceed(request);
        }
        System.out.println("A处理了");
        return new Result(true);
    }
} 

public class BHandler implements Handler {
    public Result deal(Chain chain) {
        Request request = chain.request();
        // 只处理小于等于2的请求,大于2的请求被传递了
        if (request.getDays() > 2) {
            return chain.proceed(request);
        }
        System.out.println("B处理了");
        return new Result(true);
    }
}

public class CHandler implements Handler {
    public Result deal(Chain chain) {
        Request request = chain.request();
        // 只处理小于等于3的请求,大于3的请求被传递了
        if (request.getDays() > 3) {
            return chain.proceed(request);
        }
        System.out.println("C处理了");
        return new Result(true);
    }
}
  1. 测试
public class Test {

    public static void main(String[] args) {
        // new 一个链,往链上添加处理器
        Request request1 = new Request("hhy", 3);
        HandlerChain chains = new HandlerChain(request1).addHandler(new AHandler()).addHandler(new BHandler()).addHandler(new CHandler());
        Result result1 = chains.proceed(request1);
        System.out.println("结果:" + result1.isAgree());

    }
} 
  1. 结果
传入3:
返回:
C处理了
结果:true

传入2:
返回:
B处理了
结果:true

传入1:
返回:
A处理了
结果:true

3. netty中的责任链模式

用过netty的同学看到下面这个代码应该很熟悉了,非常简单netty客户端,创建连接,设置编解码器,发送请求。

public static void main(String[] args) {
    EventLoopGroup group = new NioEventLoopGroup();
    NioSocketChannel channel = new NioSocketChannel();
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(group).option(ChannelOption.TCP_NODELAY, true);
    InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(httpRequest.host), httpRequest.port);
    group.register(channel);
    channel.connect(address).sync();
    channel.pipeline().addLast("http-decoder", new HttpResponseDecoder());
    channel.pipeline().addLast("http-encoder", new HttpRequestEncoder());
    channel.pipeline().addLast("http-client", new HttpHandler(HttpClient.this));
    channel.writeAndFlush(httpRequest);
}

通过我们上面的例子,不难猜测到 channel.pipeline() 应该就是一个链,持有了channel上所有的处理器。pipeline()方法返回一个ChannelPipeline接口,我们直接看它的实现类

public class DefaultChannelPipeline implements ChannelPipeline {
    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

    @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);
            // 把handler封装成AbstractChannelHandlerContext
            newCtx = newContext(group, filterName(name, handler), handler);
            // 调用了 addLast0方法
            addLast0(newCtx);
            ...
        }
        callHandlerAdded0(newCtx);
        return this;
    }

    private void addLast0(AbstractChannelHandlerContext newCtx) {
        // 把新加入的处理器设置成尾部的前驱,原尾部的前驱设置成新处理器的后继
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }
}

通过addLast0, 我们看到DefaultChannelPipeline使用了链表的形式存储了处理器。

继续看这个类的其他方法

public class DefaultChannelPipeline implements ChannelPipeline {
    ...
    @Override
    public final Channel channel() {
        return channel;
    }

    @Override
    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }
    ...
}

返回当前的channel,处理channel上的事件。(就类似于我们上面的链里面有getRequest(),proceed()方法 )

继续跟踪invokeChannelActive方法

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
    static void invokeChannelActive(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelActive();
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelActive();
                }
            });
        }
    }
    // 调用了invokeChannelActive
    
    private void invokeChannelActive() {
        // 判断是不是要处理
        if (invokeHandler()) {
            try {
                // 处理
                ((ChannelInboundHandler) handler()).channelActive(this);
            } catch (Throwable t) {
                invokeExceptionCaught(t);
            }
        } else {
            // 事件传递
            fireChannelActive();
        }
    }
    
    @Override
    public ChannelHandlerContext fireChannelActive() {
        // 执行前需要先找到一个合适的处理器 invokeChannelActive
        invokeChannelActive(findContextInbound(MASK_CHANNEL_ACTIVE));
        return this;
    }
        
    private AbstractChannelHandlerContext findContextInbound(int mask) {
        AbstractChannelHandlerContext ctx = this;
        EventExecutor currentExecutor = executor();
        do {
            // 循环,找到一个合适的处理器并返回
            ctx = ctx.next;
        } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
        return ctx;
    }
}

其实看到这,这个链已经非常的明显了。 pipline持有处理器,AbstractChannelHandlerContext做了一些封装,使得链上的处理器能对事件进行传递和处理。

最后再看下handler的实现类

public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
    ......
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
    }
    ......
}

以这个ChannelInboundHandlerAdapter为例,在channel收到激活事件通知的时候,它调用了ctx.fireChannelActive();方法传递了事件。ctx是ChannelHandlerContext类型,很熟悉,它是个接口,我们上面看到的AbstractChannelHandlerContext是它的实现类。就这样,一个激活事件就在链上传递了起来。而链上的处理器就是我们最初始的测试方法里面addLast进去的。

4、思考

在netty中,事件在责任链中有序传播,事件处理器可以处理自己关心的功能,可以拦截,也可以继续传播(向前或向后)事件。上层的业务只需要关心自己的逻辑。整个架构层次分明。

OA系统的工作流 似乎也特别适合责任链模式,正如我们一开始的例子,不同人审批不同的时长的假期。

对于代码里某些冗长的if else ,是不是也有改造成责任链的可能?

if (通过第一关) {
    进入第二关
    if (通过第二关) {
        进入第三关
        ...
    }
}

改成通过第一关后传递事件,反之结束。这期间还可以灵活的做跳跃,得到某个奖励,直接跳到第三关。如果要添加第四关,也比if else灵活的多。

当然并不是if else 都适合改造成责任链模式,还是要具体的业务及效率上综合考虑。个人觉得在流程上有顺序依赖的,非常适合。


[1] https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html
[2] https://blog.csdn.net/u012810020/article/details/71194853

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值