Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】

前言:责任链(chain of responsibility)模式很像异常的捕获和处理,当一个问题发生的时候,当前对象看一下自己是否能够处理,不能的话将问题抛给自己的上级去处理,但是要注意这里的上级不一定指的是继承关系的父类,这点和异常的处理是不一样的。所以可以这样说,当问题不能解决的时候,将问题交给另一个对象去处理,就这样一直传递下去直至当前对象找不到下线了,处理结束。如下图所示,处于同等层次的类都继承自Support类,当当前对象不能处理的时候,会根据预先设定好的传递关系将问题交给下一个人,可以说是“近水楼台先得月”,就看有没有能力了。我们也可以看作是大家在玩一个传谜语猜谜底的小游戏,按照座位的次序以及规定的顺序传递,如果一个人能回答的上来游戏就结束,否则继续向下传,如果所有人都回答不出来也会结束。这样或许才是责任链的本质,体现出了同等级的概念。

Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】_第1张图片

一、相关文章推荐

  1. Linux内核——红黑树的C++实现(内含源码)
  2. Linux内核——红黑树的原理和算法详细介绍(内含代码演示)
  3. 数据架构与算法——字符串转数字的hash函数与布隆过滤器
  4. 数据架构与算法———B树与B+原理和算法详细介绍(含图解简单易懂)
  5. Linux下的设计模式——策略模式(StrategyPattern)深入浅出【建议新手收藏】
  6. Linux下的设计模式——委派模式(Delegate Pattern)深入浅出【建议新手收藏】

二、责任链模式实现的三种方式

责任链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系, 将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。这里就不再过多的介绍什么是责任链模式,主要来说说java中如何编写。

主要从下面3个框架中的代码中介绍:

  • servlet中的filter
  • dubbo中的filter
  • mybatis中的plugin 这3个框架在实现责任链方式不尽相同。
1.servlet中的Filter

servlet中分别定义了一个 Filter和FilterChain的接口,核心代码如下:

public final class ApplicationFilterChain implements FilterChain {
    private int pos = 0; //当前执行filter的offset
    private int n; //当前filter的数量
    private ApplicationFilterConfig[] filters;  //filter配置类,通过getFilter()方法获取Filter
    private Servlet servlet
  
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = filterConfig.getFilter();
            filter.doFilter(request, response, this);
        } else {
            // filter都处理完毕后,执行servlet
            servlet.service(request, response);
        }
    }
  
}

代码还算简单,结构也比较清晰,定义一个Chain,里面包含了Filter列表和servlet,达到在调用真正servlet之前进行各种filter逻辑。
Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】_第2张图片

2.Dubbo中的Filter

Dubbo在创建Filter的时候是另外一个方法,通过把Filter封装成 Invoker的匿名类,通过链表这样的数据结构来完成责任链,核心代码如下:

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    //只获取满足条件的Filter
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i --) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {
                ...
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }
                ...
            };
        }
    }
    return last;
}

Dubbo的责任链就没有类似FilterChain这样的类吧Filter和调用Invoker结合起来,而是通过创建一个链表,调用的时候我们只知道第一个节点,每个节点包含了下一个调用的节点信息。 这里的虽然Invoker封装Filter没有显示的指定next,但是通过java匿名类和final的机制达到同样的效果。
Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】_第3张图片

3.Mybatis中的Plugin

Mybatis可以配置各种Plugin,无论是官方提供的还是自己定义的,Plugin和Filter类似,就在执行Sql语句的时候做一些操作。Mybatis的责任链则是通过动态代理的方式,使用Plugin代理实际的Executor类。(这里实际还使用了组合模式,因为Plugin可以嵌套代理),核心代码如下:

public class Plugin implements InvocationHandler{
    private Object target;
    private Interceptor interceptor;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {      
        if (满足代理条件) {
            return interceptor.intercept(new Invocation(target, method, args));
        }
        return method.invoke(target, args);     
    }
   
    //对传入的对象进行代理,可能是实际的Executor类,也可能是Plugin代理类
    public static Object wrap(Object target, Interceptor interceptor) {
  
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, signatureMap));
        }
        return target;
    }
} 

简单的示意图如下:
Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】_第4张图片
这里简单介绍了Servlet、Dubbo、Mybatis对责任链模式的不同实现手段,其中Servlet是相对比较清晰,又易于实现的方式,而Dubbo和Mybatis则适合在原有代码基础上,增加责任链模式代码改动量最小的。

二、代码演示

Trouble 类:(数据结构)

1 package zyr.dp.cor;
 2 
 3 public class Trouble {
 4 
 5     private int number;
 6     public Trouble( int number){
 7         this.number=number;
 8     }
 9     public int getNumber() {
10         return number;
11     }
12     public String toString(){
13         return "问题编号:["+number+"]";
14     }
15 }

Support 类:(抽象类,使用了模板方法)

package zyr.dp.cor;

public abstract class Support {

    protected abstract boolean resolve(Trouble trouble);

    String name;
    Support next;

    public Support(String name){
        this.name=name;
    }

    public String toString() {
        return "对象:<"+name+">";
    }

    public Support setAndReturnNext(Support next){
        this.next=next;
        return next;
    }

    public final void support(Trouble trouble){
        if(resolve(trouble)){
            done(trouble);
        }else if(next!=null){
            next.support(trouble);
        }else{
            fail(trouble);
        }
    }

    protected void fail(Trouble trouble) {
        System.out.println(this+"解决问题失败,"+trouble);
    }

    protected void done(Trouble trouble) {
        System.out.println(this+"已经解决问题,"+trouble);
    }

}

NoSupport类:

1 package zyr.dp.cor;
 2 
 3 public class NoSupport extends Support {
 4 
 5     public NoSupport(String name) {
 6         super(name);
 7     }
 8 
 9     protected boolean resolve(Trouble trouble) {
10         return false;
11     }
12 
13 }

OddSupport 类:

package zyr.dp.cor;
 2 
 3 public class OddSupport extends Support {
 4 
 5     public OddSupport(String name) {
 6         super(name);
 7     }
 8 
 9     protected boolean resolve(Trouble trouble) {
10         return (trouble.getNumber()%2) == 1 ? true : false;
11     }
12 
13 }

SpecialSupport类:

package zyr.dp.cor;
 2 
 3 public class SpecialSupport extends Support {
 4 
 5     public int specialNumber;
 6     public SpecialSupport(String name,int specialNumber) {
 7         super(name);
 8         this.specialNumber= specialNumber;
 9     }
10 
11     protected boolean resolve(Trouble trouble) {
12         return trouble.getNumber()==specialNumber ? true : false;
13     }
14 
15 }

LimitSupport类:

package zyr.dp.cor;
 2 
 3 public class LimitSupport extends Support {
 4 
 5     private int limit;
 6     public LimitSupport(String name,int limit) {
 7         super(name);
 8         this.limit=limit;
 9     }
10 
11     protected boolean resolve(Trouble trouble) {
12         return trouble.getNumber()<=limit? true : false;
13     }
14 
15 }

Main类:

package zyr.dp.cor;
 2 
 3 public class Main {
 4 
 5     public static void main(String[] args) {
 6         Support limitSupportLess = new LimitSupport("有限支持小",5);
 7         Support limitSupportMore = new LimitSupport("有限支持大",15);
 8         Support oddSupport = new OddSupport("奇数支持");
 9         Support specialSupport = new SpecialSupport("特定支持",36);
10         Support noSupport = new NoSupport("没有支持");
11         limitSupportLess.setAndReturnNext(limitSupportMore).setAndReturnNext(oddSupport).setAndReturnNext(specialSupport).setAndReturnNext(noSupport);
12         System.out.println("===<有限支持小>尝试解决问题===");
13         for(int i=0;i<40;i++){
14             limitSupportLess.support(new Trouble(i));
15         }
16         System.out.println("===<特定支持>尝试解决问题===");
17         for(int i=0;i<40;i++){
18             specialSupport.support(new Trouble(i));
19         }
20 
21     }
22 
23 }

运行结果:

我们可以看到同等级的对象,按照自己被添加的次序来安排,这点非常重要,在实际应用中,我们都是将解答能力最弱的类放到最前面,然后一点点加强,这样可以使得解答能力比较弱的类有机会去解答,正如我们的例子,如果让解答能力强的类直接去处理问题,能够处理就不回传给下一个了,当然我们也看到这里面有的类能力有限,有的类和其他类的能力有重叠部分,当然也有所有类都解决不了的问题。

通过责任链,我们可以将问题与处理问题的对象分离出来,特别适合用于不知道发生的问题到底是什么(有很多选择)但是又必须处理的情况(按照每个选择情况设计相应的处理类),这样就弱化了请求问题的人和回答问题的人的关系,如果不得不让发出请求的人知道处理请求的人,这样就不利于代码的复用,因为需要在请求问题的对象之中调用处理请求的对象,而使用责任链模式就能很好的规避这一点,便于将这部分代码独立出来,当做组件使用,符合开闭原则。

同样的我们看到在责任链中使用了模板方法,在父类中抽象的定义了将要处理的流程,使得扩充起来非常方便,修改起来也很方便。利用委托的思想,不过这次是自己使用自己,通过责任链,其实就是一个链表,来处理问题是非常好的一种思想。可以动态地修改责任链,同时也使得每个责任链上的对象可以专注于自己的问题,这样思路清晰,便于扩展,但是使用责任链模式因为不断地调用能够处理某个问题的对象(遍历链表)和直接就能知道谁去处理的方式相比肯定会出现延迟,这就需要一个取舍了。

最后,我们要注意,resolve()的属性是protected,这样做的好处是可以让同一个包或者子类使用该方法,而不会让其他包的类使用,因为解决问题的方法就应该是子类使用的,这样可以使得该方法不被误用。而将失败和成功方法设为protected,也是可以让子类去继承和修改,而不让其他包中的类使用,对于support()方法就是可以让其他包使用的,因此使用public,这一点可以看出写代码的严谨度。

四、总结

责任链使用了模板方法和委托的思想构建了一个链表,通过遍历链表来一个个询问链表中的每一个节点谁可以处理某件事情,如果某个节点能够胜任,则直接处理,否则继续向下传递,如果都不能处理next==null,则处理结束。责任链会造成处理的时延,但是能够解耦合,提高可扩展性,使得处理方法的类更专注于处理把自己的事情,便于扩展和改变处理的优先级,应用也非常的广泛。

还等什么小编推荐自己的linuxC/C++语言交流群:【1106675687】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!前100名进群领取,额外赠送一份价值199的C/C++、linux资料包含(视频教程、电子书、实战项目及代码),下面部分展示。

Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】_第5张图片
Linux下的设计模式——责任链模式(Chain of Responsibility)深入浅出【建议新手收藏】_第6张图片

你可能感兴趣的:(c++,设计模式,编程语言,spring,linux)