文章

责任链模式基础

责任链模式基础

责任链模式

责任链模式定义

是一个请求有多个对象来处理,这些对象是一条链,但具体由哪个对象来处理,根据条件判断来确定,如果不能处理会传递给该链中的下一个对象,直到有对象处理它为止。

将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
以上定义来自《设计模式之美》

官方图解:
6meay

  1. Client(客户端):实例化一个处理器的链,在第一个链对象中调用 handleRequest 方法。
  2. Handle(处理器):抽象类,提供给实际处理器继承然后实现 handleRequst 方法,处理请求
  3. ConcreteHandler(具体处理器):继承了 handler 的类,同时实现 handleRequst 方法,负责处理业务逻辑类,不同业务模块有不同的 ConcreteHandler。

使用场景

  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定
  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  • 可动态指定一组对象处理请求,客户端可以动态创建责任链来处理请求

责任链模式实际使用场景

拦截器

一条 chain,其中有一个处理者处理,chain 断开

  • 聊天 Message 处理
  • 流程审批

默认处理者要放在最后一个

过滤器

一条 chain,链上的处理者可以对输入数据进行处理,交由下一个处理

责任链拦截器模板 (参考 OKHttp)

Interceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Interceptor<T, R> {

    R intercept(Chain<T, R> chain) throws Exception;

    // Chain作为容器,为Interceptor携带input参数
    interface Chain<T, R> {
        T request(); // 输入参数,可以定义成多个

        // 某条链上的处理动作,input为输入数据,R为输出
        R proceed(T input) throws Exception;
    }

}

RealInterceptorChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class RealInterceptorChain<T, R> implements Interceptor.Chain<T, R> {

    private List<Interceptor> interceptors;
    private int index;
    private T input;

    public RealInterceptorChain(List<Interceptor> interceptors, int index, T input) {
        this.interceptors = interceptors;
        this.index = index;
        this.input = input;
    }

    @Override
    public T request() {
        return input;
    }

    @Override
    public R proceed(T input) throws Exception {
        if (interceptors == null) {
            throw new IllegalArgumentException("chains must not null!");
        }
        if (index < 0) {
            throw new IllegalArgumentException("index must be positive!");
        }
        if (index >= interceptors.size()) {
            throw new IllegalArgumentException("should a default interceptor not call proceed() , interceptors size: " + interceptors.size() + " index: " + index);
        }
        // 每经过一个链条,就是一个新的RealInterceptorChain
        RealInterceptorChain<T, R> next = new RealInterceptorChain<T, R>(interceptors, index + 1, input);
        Interceptor<T, R> interceptor = interceptors.get(index);
        return interceptor.intercept(next);
    }

}

InterceptorHelper

1
2
3
4
5
6
7
8
9
10
11
public final class InterceptorHelper {

    public static <T, R> R build(T input, List<Interceptor<T, R>> interceptors) {
        RealInterceptorChain<T, R> chain = new RealInterceptorChain(interceptors, 0, input);
        try {
           return chain.proceed(input);
        } catch (Exception e) {
        }
        return null;
    }
}

最后一个 Interceptor 不要调用 chain.proceed,否则会数组越界异常

使用了责任链模式的库

责任链应用

责任链模式案例 1- 流程审批

1
2
3
4
员工请求审批流程,
组长可以批假不超过2天,
项目经理可以批假不超过10天,
老板可以批假不超过30天的假。

普通的责任链

  • 管理者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * 管理者统称
 *
 * 员工请求审批流程: 组长可以批假不超过2天, 项目经理可以批假不超过10天, 老板可以批假不超过30天的假。
 */
public abstract class Manager {

    protected String name;
    private Manager successor;

    public Manager(String name, Manager successor) {
        this.name = name;
        this.successor = successor;
    }

    public Manager getSuccessor() {
        return successor;
    }

    /**
     * 处理请假审批
     *
     * @param user 请假者
     * @param day  请假天数
     */
    public abstract boolean handleRequestApprove(String user, int day);
}
  • 组长
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 组长
 */
public final class GroupManager extends Manager {

    public GroupManager(String name, Manager successor) {
        super(name, successor);
    }

    @Override
    public boolean handleRequestApprove(String user, int day) {
        if (day <= 2) {
            System.out.println("组长" + name + "可以直接审批不超过2天的假");
            return true;
        } else {
            Manager successor = getSuccessor();
            if (successor == null) {
                throw new IllegalArgumentException("组长" + name + "需要有自己的领导!");
            }
            return successor.handleRequestApprove(user, day);
        }
    }
}
  • 项目经理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 项目经理
 */
public final class ProjectManager extends Manager {
    public ProjectManager(String name, Manager successor) {
        super(name, successor);
    }
    @Override
    public boolean handleRequestApprove(String user, int day) {
        if (day <= 10) {
            System.out.println("项目经理" + name + "可以直接审批不超过10天的假");
            return true;
        } else {
            Manager successor = getSuccessor();
            if (successor == null) {
                throw new IllegalArgumentException("项目经理" + name + "需要有自己的领导!");
            }
            return successor.handleRequestApprove(user, day);
        }
    }
}
  • 老板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * 老板
 */
public final class BossManager extends Manager {
    public BossManager(String name) {
        super(name, null);
    }
    @Override
    public boolean handleRequestApprove(String user, int day) {
        if (day <= 30) {
            System.out.println("老板" + name + "只能审批不超过30天的假");
            return true;
        } else {
            System.out.println("兄弟啊凉了啊,请这么久,直接打包回家吧!");
            return false;
        }
    }
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final class TestChains {

    public static void main(String[] args) {
        buildChains();

        String user = "苦逼的猿";
        boolean requestApprove = buildChains().handleRequestApprove(user, 33);
        if (requestApprove) {
            System.out.println("审批了");
        } else {
            System.out.println("没人审批");
        }
    }

    private static Manager buildChains() {
        Manager bossManager = new BossManager("曾大圣");
        Manager projectMananger = new ProjectManager("Elain", bossManager);
        Manager manager = new GroupManager("chencheng", projectMananger);
        return manager;
    }
}

类似 OkHttp Interceptor 方式

需要手动设置链的下一个处理者,用类似 OkHttp Interceptor 的责任链来优化下

  • ManagerV2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ManagerV2,类似OkHttp中Interceptor
public interface ManagerV2 {
    /**
     * 处理请假请求
     * @param chain Chain,需要包含ManagerV2所需要的输入参数
     * @return 是否有人审批请假请求
     */
    boolean handleRequestApprove(Chain chain);
    interface Chain { 类似OkHttp中Chain
        // 输入参数1,类似OkHttp中Request
        String getUser();
        // 输入参数2
        int getDay();
        // 供ManagerV2在handleRequestApprove()调用,表示不处理该请求,交由Chain中的下一个处理者ManagerV2来处理
        boolean proceed(String user, int day);
    }
}
  • RealChainManager,链 chain 的构造,即 chain 下一个处理者的调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public final class RealChainManager implements ManagerV2.Chain {
    List<ManagerV2> mManagerV2s;
    private int index;
    private String user;
    private int day;
    public RealChainManager(List<ManagerV2> managerV2s, int index, String user, int day) {
        mManagerV2s = managerV2s;
        this.index = index;
        this.user = user;
        this.day = day;
    }
    @Override
    public String getUser() {
        return user;
    }
    @Override
    public int getDay() {
        return day;
    }
    @Override
    public boolean proceed(String user, int day) {
        if (index > mManagerV2s.size()) {
            System.out.println("无人审批,审批不了:" + index);
            return false;
        }
        ManagerV2.Chain next = new RealChainManager(mManagerV2s, index + 1, user, day);
        // 当前审批者
        ManagerV2 managerV2 = mManagerV2s.get(index);
        boolean b = managerV2.handleRequestApprove(next);
        return b;
    }

}

具体的管理者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// GroupManagerV2
public class GroupManagerV2 implements ManagerV2 {
    @Override
    public boolean handleRequestApprove(Chain chain) {
        String user = chain.getUser();
        int day = chain.getDay();
        if (day <= 2) {
            System.out.println("组长可以直接审批" + user + "不超过2天的假");
            return true;
        }
        return chain.proceed(user, day);
    }
}

// ProjectManagerV2
public class ProjectManagerV2 implements ManagerV2 {
    @Override
    public boolean handleRequestApprove(Chain chain) {
        String user = chain.getUser();
        int day = chain.getDay();
        if (day <= 10) {
            System.out.println("项目经理可以直接审批" + user + "不超过10天的假");
            return true;
        }
        return chain.proceed(user, day);
    }
}

// BossManagerV2
public class BossManagerV2 implements ManagerV2 {
    @Override
    public boolean handleRequestApprove(Chain chain) {
        String user = chain.getUser();
        int day = chain.getDay();
        if (day <= 30) {
            System.out.println("老板可以直接审批" + user + "不超过30天的假");
            return true;
        }
        return false;
    }
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void testChain2() {
    String user = "hacket";
    int day = 35;
    List<ManagerV2> managerV2s = new ArrayList<>();
    managerV2s.add(new GroupManagerV2());
    managerV2s.add(new ProjectManagerV2());
    managerV2s.add(new BossManagerV2());
    RealChainManager chain = new RealChainManager(managerV2s, 0, user, day); // 构造chain
    boolean proceed = chain.proceed(user, day); // 交由链来处理,其中有一个处理,那么该链就终止
    if (!proceed) {
        System.out.println("无人审批啊");
    }
}

责任链模式案例 2- 对这段文本进行加工过滤

假设这样的场景:传入了一段内容,需要对这段文本进行加工;比如过滤敏感词、错别字修改、最后署上版权等操作。

定义接口 TextProcessFilter

1
2
3
4
5
6
7
public interface TextProcessFilter {
    Text doProcess(Chain chain);
    interface Chain {
        Text getInput();
        Text proceed(Text originText);
    }
}

RealTextProcessFilterChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class RealTextProcessFilterChain implements TextProcessFilter.Chain {

    private List<TextProcessFilter> textProcessFilters;
    private int index;
    private Text input;

    public RealTextProcessFilterChain(List<TextProcessFilter> textProcessFilters, int index, Text input) {
        this.textProcessFilters = textProcessFilters;
        this.index = index;
        this.input = input;
    }

    @Override
    public Text getInput() {
        return input;
    }

    @Override
    public Text proceed(Text originText) {
        if (textProcessFilters == null) {
            throw new IllegalArgumentException("textProcessFilters不能为空!");
        }
        if (index >= textProcessFilters.size()) {
            throw new IllegalArgumentException("越界了:index:" + index + ",size:" + textProcessFilters.size());
        }
        TextProcessFilter.Chain next = new RealTextProcessFilterChain(textProcessFilters, index + 1, input);
        TextProcessFilter textProcessFilter = textProcessFilters.get(index);
        return textProcessFilter.doProcess(next);
    }

}

实际的处理者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 英文全部大写,ToUpperTextFilter
public class ToUpperTextFilter implements TextProcessFilter {
    @Override
    public Text doProcess(Chain chain) {
        Text input = chain.getInput();
        String s = input.text.toUpperCase();
        input.text = s;
        return chain.proceed(input);
    }
}
// 过滤色情字符,YellowTextFilter
public class YellowTextFilter implements TextProcessFilter {
    @Override
    public Text doProcess(Chain chain) {
        Text input = chain.getInput();
        String text = input.text;
        String result = text.replace("色", "***");
        input.text = result;
        return chain.proceed(input);
    }
}
// 版权信息,CopyrightTextFilter
public class CopyrightTextFilter implements TextProcessFilter {
    @Override
    public Text doProcess(Chain chain) {
        Text input = chain.getInput();
        StringBuilder sb = new StringBuilder();
        sb.append(input.text);
        sb.append("-->>曾大圣,版权所有©️");
        input.text = sb.toString();
        return input; // 最后一个不要proceed了
    }
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void testToUpperTextFilter() {
    List<TextProcessFilter> processFilters = new ArrayList<>();
    processFilters.add(new ToUpperTextFilter());
    processFilters.add(new YellowTextFilter());
    processFilters.add(new CopyrightTextFilter());

    Text text = new Text("你猜我是谁,我是hacket?,哈哈,颜色");

    System.out.println("处理前:" + text.text);

    RealTextProcessFilterChain chain = new RealTextProcessFilterChain(processFilters, 0, text);
    Text proceedText = chain.proceed(text);

    System.out.println("处理后:" + proceedText.text);
}

结果:

1
2
处理前:你猜我是谁,我是hacket?,哈哈,颜色
处理后:你猜我是谁,我是HACKET?,哈哈,颜***-->>曾大圣,版权所有©️

责任链模式案例 3- 网络请求错误码全局处理

  1. 错误码处理

接口 IErrorHandler 定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
interface IErrorHandler {

    @Throws(CustomHttpException::class)
    fun handle(chain: Chain) 

    // 由Chain定义Handler需要处理的input参数,由proceed返回值为output,chain为一条链
    interface Chain {
        fun getErrCode(): Int // Handler的input

        fun getErrMsg(): String // Handler的input

        @Throws(CustomHttpException::class)
        fun proceed(errCode: Int, errMsg: String) // 返回值可以定义ouput
    }
    companion object {

        const val TAG = "net.error"

        /**
         * Token过期
         */
        const val ERROR_TOKEN_EXPIRE = -2
        /**
         * 在其他设备登录
         */
        const val ERROR_LOGIN_OTHER_DEVICE = -4
        /**
         * 用户被封禁
         */
        const val ERROR_USER_BANNED = -5
    }
}

RealErrorHandlerChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class RealErrorHandlerChain(
        private val mHandlerList: List<IErrorHandler>?,
        private val index: Int,
        private val errCode: Int,
        private val errMsg: String) : IErrorHandler.Chain {

    override fun getErrCode(): Int {
        return errCode
    }

    override fun getErrMsg(): String {
        return errMsg
    }

    @Throws(CustomHttpException::class)
    override fun proceed(errCode: Int, errMsg: String) {
        if (mHandlerList == null) {
            throw IllegalArgumentException("mHandlerList不能为空!")
        }
        if (index >= mHandlerList.size) {
            throw IllegalArgumentException("越界了:index:" + index + ",size:" + mHandlerList.size)
        }
        val next = RealErrorHandlerChain(mHandlerList, index + 1, errCode, errMsg)
        val handler = mHandlerList[index]
        handler.handle(next)
    }
}

具体的 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class LogoutErrorHandler : IErrorHandler {

    @Throws(CustomHttpException::class)
    override fun handle(chain: IErrorHandler.Chain) {
        val errCode = chain.getErrCode()
        val errMsg = chain.getErrMsg()
        when (errCode) {
            IErrorHandler.ERROR_TOKEN_EXPIRE,
            IErrorHandler.ERROR_LOGIN_OTHER_DEVICE,
            IErrorHandler.ERROR_USER_BANNED -> {
                ToastUtils.showLong(errMsg)
                gotoLoginPage()
                LogUtils.e(TAG, "${anchor("handle")}token过期回到登录页面," +
                        "errCode=$errCode,errMsg=$errMsg")
            }
            else -> {
                chain.proceed(errCode, errMsg)
            }
        }
    }

    private fun gotoLoginPage() {
        UserCenterManager.logout(GlobalContext.getAppContext())
        ARouter.getInstance()
                .build(ARouterConstants.Login.ROUTER_PATH_ACTIVITY_LOGIN)
                .withFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
                .navigation()
    }
}

class DefaultErrorHandler : IErrorHandler {
    @Throws(CustomHttpException::class)
    override fun handle(chain: IErrorHandler.Chain) {
        val errCode = chain.getErrCode()
        val errMsg = chain.getErrMsg()
        LogUtils.w(IErrorHandler.TAG, "DefaultErrorHandler,errCode=$errCode,errMsg=$errMsg")
        throw CustomHttpException(errMsg, errCode)
    }
}

使用

1
2
3
4
5
List<IErrorHandler> handlers = new ArrayList<>();
handlers.add(new LogoutErrorHandler());
handlers.add(new DefaultErrorHandler());
RealErrorHandlerChain chain = new RealErrorHandlerChain(handlers, 0, error, errorMsg);
chain.proceed(error, errorMsg);
本文由作者按照 CC BY 4.0 进行授权