Skip to content

Latest commit

 

History

History
219 lines (168 loc) · 7.25 KB

责任链模式.md

File metadata and controls

219 lines (168 loc) · 7.25 KB

责任链模式

定义

百度百科的介绍:责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

维基百科的介绍:责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。

我的介绍:顾名思义,责任链模式是一条链,链上有多个节点,每个节点都有各自的责任。当有输入时,第一个责任节点看自己能否处理该输入,如果可以就处理。如果不能就交由下一个责任节点处理。依次类推,直到最后一个责任节点。

定义总是有点文绉绉,还是看下下面的例子加深下理解吧。

例子

  1. 需求开发例子

    假设现在有个需求来了,首先是实习生拿到这个需求。
    如果实习生能够实现,直接实现。如果不行,他把这个需求交给初级工程师。
    如果初级工程师能够实现,直接实现。如果不行,交给中级工程师。
    如果中级工程师能够实现,直接实现。如果不行,交给高级工程师。
    如果高级工程师能够实现,直接实现。如果不行,交给 CTO。
    如果 CTO能够实现,直接实现。如果不行,直接跟产品说,需求不做。

  2. 买球篮例子

    假设你现在有个篮球,然后想要买个球篮。
    你肯定是到店里,让老板把所有尺寸的球篮拿出来。
    然后你一个一个试。
    第一个不行,就第二个。
    第二个不行,就第三个。
    ...
    直到找到合适的。

传统代码

给定一个输入值,根据输入值执行不同逻辑。

String input = "1";
if ("1".equals(input)) {
    //TODO do something
} else if ("2".equals(input)) {
    //TODO do something
} else if ("3".equals(input)) {
    //TODO do something
}

或者如下代码:

String input = "1";
switch (input) {
    case "1":
        //TODO do something
        break;
    case "2":
        //TODO do something
        break;
    case "3":
        //TODO do something
        break;
    default:
        //TODO do something
        break;
}

如果每个分支里面的逻辑比较简单,那还好,如果逻辑复杂,假设每个 case 大概要 100 行代码处理,有 10 个 case,一下子就出来一个「千行代码」文件,而且还不利于维护、测试和扩展。

下面来介绍如何通过责任链模式的妙用来拆分代码。

责任链模式代码

这里以上面场景为例子进行拆分代码说明。

  1. 定义一个抽象类。

    public abstract class BaseCase {
        // 为 true 表明自己可以处理该 case
        private boolean isConsume;
    
        public BaseCase(boolean isConsume) {
            this.isConsume = isConsume;
        }
    
        // 下一个责任节点
        private BaseCase nextCase;
    
        public void setNextCase(BaseCase nextCase) {
            this.nextCase = nextCase;
        }
    
        public void handleRequest() {
            if (isConsume) {
                // 如果当前节点可以处理,直接处理
                doSomething();
            } else {
                // 如果当前节点不能处理,并且有下个节点,交由下个节点处理
                if (null != nextCase) {
                    nextCase.handleRequest();
                }
            }
        }
    
        abstract protected void doSomething();
    }
  2. 定义多个 case 来实现该抽象类,如下所示:

    public class OneCase extends BaseCase {
        public OneCase(boolean isConsume) {
            super(isConsume);
        }
    
        @Override
        protected void doSomething() {
            // TODO do something
            System.out.println(getClass().getName());
        }
    }
  3. 初始化各个 case,并指定每个 case 的下一个节点。

    String input"1";
    OneCase oneCasenew OneCase("1".equals(input));
    TwoCase twoCasenew TwoCase("2".equals(input));
    DefaultCase defaultCasenew DefaultCase(true);
    oneCase.setNextCase(twoCase);
    twoCase.setNextCase(defaultCase);
    oneCase.handleRequest();

优化

参考 OkHttp 里面的 Interceptor 实现,将所有的 case 集中起来,通过遍历确定能够处理的 case。

  1. 定义一个接口。

    interface BaseCase {
        // 所有 case 处理逻辑的方法
        void doSomething(String input, BaseCase baseCase);
    }
  2. 建立一个责任链管理类,管理所有 case。

    public class CaseChain implements BaseCase {
        // 所有 case 列表
        private List<BaseCase> mCaseList = new ArrayList<>();
        // 索引,用于遍历所有 case 列表
        private int            index     = 0;
    
        // 添加 case
        public CaseChain addBaseCase(BaseCase baseCase) {
            mCaseList.add(baseCase);
            return this;
        }
    
        @Override
        public void doSomething(String input, BaseCase baseCase) {
            // 所有遍历完了,直接返回
            if (index == mCaseList.size()) return;
            // 获取当前 case
            BaseCase currentCase = mCaseList.get(index);
            // 修改索引值,以便下次回调获取下个节点,达到遍历效果
            index++;
            // 调用当前 case 处理方法
            currentCase.doSomething(input, this);
        }
    }
  3. 各个 case 实现接口。这里以其中一个为例。

    public class OneCase implements BaseCase {
        @Override
        public void doSomething(String input, BaseCase baseCase) {
            if ("1".equals(input)) {
                // TODO do something
                System.out.println(getClass().getName());
                return;
            }
            // 当前没法处理,回调回去,让下一个去处理
            baseCase.doSomething(input, baseCase);
        }
    }
  4. 初始化各个 case

    String input"1";
    CaseChain caseChainnew CaseChain();  
    caseChain.addBaseCase(new OneCase())
                 .addBaseCase(new TwoCase())
                 .addBaseCase(new DefaultCase());  
    caseChain.doSomething(input, caseChain);

总结

其实见到该模式就应该能联想到数据结构的链表,所谓的处理就是遍历一遍该列表筛选出符合的就让它去处理即可。

结语

我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————安卓 offer 收割基,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。