dushaoshuai / dushaoshuai.github.io

https://www.shuai.host
0 stars 0 forks source link

Chain of Responsibility(CoR, Chain of Command) #66

Open dushaoshuai opened 1 year ago

dushaoshuai commented 1 year ago

若干个实现了相同接口的 handler 串成一条职责链,请求在链条中向下传递,每个 handler 都可以决定是否处理请求以及是否将请求传递给下一个 handler。

有两种模式:

一种函数式编程实现

关于面向对象的写法,我之前写过一个。在这种写法中,每个 handler 都要保存下一个 handler 的引用,不然它无法向下传递。

这里分析的主要是函数式编程的实现方法。这是一段来自 github.com/grpc/grpc-go 的代码,做了一些简化:

// Handler defines the handler invoked by Interceptor to complete the normal execution.
type Handler func(ctx context.Context, req any) (resp any, err error)

// Interceptor provides a hook to intercept the execution.
// It is the responsibility of the interceptor to invoke handler to complete the execution.
type Interceptor func(ctx context.Context, req any, handler Handler) (resp any, err error)

// chainInterceptors chains interceptors into one. The first interceptor will be the outer most,
// while the last interceptor will be the inner most wrapper around handler.
func chainInterceptors(interceptors []Interceptor) Interceptor {
    return func(ctx context.Context, req any, handler Handler) (resp any, err error) {
        return interceptors[0](ctx, req, getChainedHandler(interceptors, 0, handler))
    }
}

func getChainedHandler(interceptors []Interceptor, curr int, finalHandler Handler) Handler {
    if curr == len(interceptors)-1 {
        return finalHandler
    }
    return func(ctx context.Context, req any) (resp any, err error) {
        return interceptors[curr+1](ctx, req, getChainedHandler(interceptors, curr+1, finalHandler))
    }
}

若干 InterceptorHandler 串成了一条责任链。请求经过 Interceptor 的拦截处理,最后由 Handler 完成真正的响应。每个 Interceptor 都可以决定是否调用下一个 Interceptor,最后一个 Interceptor 可以决定是否调用 Handler

chainInterceptors 将多个 Interceptor 组合成一个 Interceptor。前一个 Interceptor 包裹后一个 Interceptor,所有的 Interceptor 层层包裹,第一个 Interceptor 在最外面,最后一个在最里面(是洋葱模型?)。请求到达时,先由外向内进入,再由内向外返回。

image

See also

https://refactoring.guru/design-patterns/chain-of-responsibility