• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

图解设计模式(14):Chain of Responsibility模式(推卸责任)

设计模式 winrains 1年前 (2019-09-23) 36次浏览

将多个对象组成一条职责链,然后按照它们在职责链上的顺序一个一个地找出到底应该由谁来负责处理的模式被称为Chain of Responsibility模式。
使用Chain of Responsibility模式可以弱化“请求方”和“处理方”之间的关联系统,让双方各自都成为可独立复用的组件。
当一个人被要求做什么事情时,如果他可以做就自己做,如果不能做就将“要求”转给另外一个人。下一个人如果可以自己处理,就自己做;如果也不能自己处理,就再转给另外一个人……这就是Chain of Responsibility模式。

1 Chain of Responsibility模式中的角色

  • Handler(处理者)

定义了处理请求的接口。Handler角色知道“下一个处理者”是谁。如果自己无法处理请求,它会将请求转给“下一个处理者”。当然,”下一个处理者“也是Hander角色。在示例中,对应Support类,处理处理请求的是support方法。

  • ConcreteHandler(具体的处理者)

处理请求的具体角色。在示例中,对应NoSupport、LimitSupport、OddSupport、SecialSupport等各个类。

  • Client(请求者)

向每一个ConcreteHandler角色发送请求的角色。在示例中,对应Main类。

2 Chain of Responsibility模式的类图

3 示例程序

3.1 类一览表

名字 说明
Trouble 表示发生的问题的类。它带有问题编号
Support 用来解决问题的抽象类
NoSupport 用来解决问题的具体类(永远不处理问题)
LimitSupport 用来解决问题的具体类(仅解决编号小于指定编号的问题)
OddSupport 用来解决问题的具体类(仅解决奇数编号的问题)
SpecialSupport 用来解决问题的具体类(仅解决指定编号的问题)
Main 制作Support职责链,制造问题并测试程序行为

3.2 类图

3.3 示例代码

Trouble类

表示发生问题的类。number是具体编码。

public class Trouble {
    private int number;             // 问题编号
    public Trouble(int number) {    // 生成问题
        this.number = number;
    }
    public int getNumber() {        // 获取问题编号
        return number;
    }
    public String toString() {      // 代表问题的字符串
        return "[Trouble " + number + "]";
    }
}

Support类

是用来解决问题的抽象类,它是职责链上的对象。
next字段中指定要推卸给的对象。可以通过setNext方法设定该对象。
resolve方法是需要子类去实现的抽象方法。
support方法会调用resoleve方法,如果resolve方法返回false,则support方法会将问题交给下一个对象。如果已经到达职责链中的最后一个对象,则表示没有人处理问题,将会显示出处理失败的相关信息。support方法调用了抽象方法resolve,属于Template Method模式。

public abstract class Support {
    private String name;                    // 解决问题的实例的名字
    private Support next;                   // 要推卸给的对象
    public Support(String name) {           // 生成解决问题的实例
        this.name = name;
    }
    public Support setNext(Support next) {  // 设置要推卸给的对象
        this.next = next;
        return next;
    }
    public void support(Trouble trouble) {  // 解决问题的步骤
        if (resolve(trouble)) {
            done(trouble);
        } else if (next != null) {
            next.support(trouble);
        } else {
            fail(trouble);
        }
    }
    public String toString() {              // 显示字符串
        return "[" + name + "]";
    }
    protected abstract boolean resolve(Trouble trouble); // 解决问题的方法
    protected void done(Trouble trouble) {  // 解决
        System.out.println(trouble + " is resolved by " + this + ".");
    }
    protected void fail(Trouble trouble) {  // 未解决
        System.out.println(trouble + " cannot be resolved.");
    }
}

NoSupport类

Support类的子类。NoSupport类的resolve方法问题返回false,即它是一个永远“不解决问题”的类。

public class NoSupport extends Support {
    public NoSupport(String name) {
        super(name);
    }
    protected boolean resolve(Trouble trouble) {     // 解决问题的方法
        return false; // 自己什么也不处理
    }
}

LimitSupport类

用于解决编号小于limit值的问题。

public class LimitSupport extends Support {
    private int limit;                              // 可以解决编号小于limit的问题
    public LimitSupport(String name, int limit) {   // 构造函数
        super(name);
        this.limit = limit;
    }
    protected boolean resolve(Trouble trouble) {    // 解决问题的方法
        if (trouble.getNumber() < limit) {
            return true;
        } else {
            return false;
        }
    }
}

OddSupport类

用于解决奇数的问题。

public class OddSupport extends Support {
    public OddSupport(String name) {                // 构造函数
        super(name);
    }
    protected boolean resolve(Trouble trouble) {    // 解决问题的方法
        if (trouble.getNumber() % 2 == 1) {
            return true;
        } else {
            return false;
        }
    }
}

SepecialSupport类

用于只解决指定编号的问题。

public class SpecialSupport extends Support {
    private int number;                                 // 只能解决指定编号的问题
    public SpecialSupport(String name, int number) {    // 构造函数
        super(name);
        this.number = number;
    }
    protected boolean resolve(Trouble trouble) {        // 解决问题的方法
        if (trouble.getNumber() == number) {
            return true;
        } else {
            return false;
        }
    }
}

Main类

测试程序。

public class Main {
    public static void main(String[] args) {
        Support alice   = new NoSupport("Alice");
        Support bob     = new LimitSupport("Bob", 100);
        Support charlie = new SpecialSupport("Charlie", 429);
        Support diana   = new LimitSupport("Diana", 200);
        Support elmo    = new OddSupport("Elmo");
        Support fred    = new LimitSupport("Fred", 300);
        // 形成职责链
        alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
        // 制造各种问题
        for (int i = 0; i < 500; i += 33) {
            alice.support(new Trouble(i));
        }
    }
}

运行结果

[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].

4 总结

4.1 弱化了发出请求的人和处理请求的人之间的关系

Chain of Responsibility模式的最大优点就在于它弱化了发出请求的人(Client角色)和处理请求的人(ConcreteHandler角色)之间的关系。Client角色向第一个ConcreteHandler角色发出的请求,然后请求会在职责链中传播,直到某个ConcreteHandler角色处理该请求。

4.2 可以动态地改变职责链

使用Chain of Responsibility模式,通过委托推卸责任,可以根据情况变化动态地重组职责链。
如果不使用该械,而是在程序中固定写明“某个请求需要谁处理“这样的对应关系,那么很难在程序运行中去改变请求的处理者。

4.3 专注于自己的工作

可以使每个对象更专注于自己的工作,即每个ConcreteHandler角色都专注于自己所负责的处理。当自己无法处理时,就可以将请求直接转出去。

4.4 推卸请求会导致处理延迟

与事先确定哪个对象负责什么样的处理相比,使用该模式会导致处理请求发生延迟。
不过,这是一个需要权衡的问题。如果请求和处理者之间的关系是确定的,而且需要非常快的处理速度时,不使用Chain of Responsibility模式会更好。

摘自《图解设计模式》


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)