Chain of Responsibility (责任链)模式

(一段时间没更新了,坚持了100天考研^^)

14.1 Chain of Responsibility (责任链)模式

  “推卸责任” 有贬义的意思,但是有时候也需要 “推卸责任”的情况。例如,当外部请求程序进行某个处理,但程序暂时无法之间决定由哪个对象负责处理时,就需要推卸责任。这种情况下,我们可以考虑将多个对象组成一条责任链,然后按照他们在责任链上的顺序一个个地找出到底谁来负责处理。
  这种模式称为 Chain of Responsibility 模式。我们可以将它想象为推卸责任的结构。这种模式可以弱化 “请求方” 和 “处理方” 之间的关联关系。让双方各自都成为可独立复用的组件。此外,程序还可以应对其他请求,如根据情况不同,负责处理的对象也会发生变化的这种需求。
  当一个人被要求做什么事情时,如果他可以做就自己做,如果不能做就将 “要求” 转给另外一个人。下一个人如果可以自己处理,就自己做;如果也不能自己处理,就再转给另外一个人…这就是 Chain of Responsibility 模式。

14.2 示例程序

  下面我们来看看使用了 Chain of Responsibility 模式的示例程序。
示例程序 UML 类图

示例程序类图
|| Trouble 类

  Trouble 类是表示发生的问题的类。number 是问题的编号。通过 getNumber 方法可以获取问题编号。

public class Trouble {

    private int number; // 问题的编号

    public Trouble(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    @Override
    public String toString() {
        return "[Trouble " + number + "]";
    }
}
|| Support 类

  Support 类是用来解决问题的抽象类,它是职责链上的对象。
  next 字段中指定了要推卸给的对象。
  resolve 方法是需要子类去实现的抽象方法(模版方法模式)。如果 resolve 返回 true,则表示问题已经被处理,如果返回 false 表示问题还没有被处理,需要推卸给下一个对象。
  support 方法会调用 resolve 方法,如果 resolve 方法返回 false,则 support 方法会将问题交给下一个对象。如果已经到达职责链中的最后一个对象,则表示没有人会处理问题,将会显示出处理失败的相关信息,有时也可以抛出异常。

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 final void support(Trouble trouble) {
        if (resolve(trouble)) {
            done(trouble);
        } else if (next != null) {
            next.support(trouble); // 继续处理
        } else {
            fail(trouble);
        }
    }

    public 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.");
    }

    @Override
    public String toString() {
        return "[" + name + "]";
    }
}
|| NoSupport 类

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

/**
* 什么也不做处理的类,只返回 false
*/
public class NoSupport extends Support {

    public NoSupport(String name) {
        super(name);
    }

    @Override
    public boolean resolve(Trouble trouble) {
        return false;
    }
}
|| LimitSupport 类

  LimitSupport 类解决编号小于 limit 值的问题。resolve 方法在判断编号小于 limit 后,只是简单的返回 true,但实际上这里应该是解决问题的代码,不过省略了。

/**
* 只处理编号小于 limit 的问题
*/
public class LimitSupport extends Support {

    private int limit;

    public LimitSupport(String name, int limit) {
        super(name);
        this.limit = limit;
    }

    @Override
    public boolean resolve(Trouble trouble) {
        if (trouble.getNumber() < limit) {
            // ...
            return true;
        }
        return false;
    }
}
|| OddSupport 类

  OddSupport 类解决奇数编号的问题。

/**
* 只解决编号为奇数的问题.
*/
public class OddSupport extends Support {

    public OddSupport(String name) {
        super(name);
    }

    @Override
    public boolean resolve(Trouble trouble) {
        if (trouble.getNumber() % 2 == 1) {
            //...
            return true;
        }
        return false;
    }
}
|| SpecialSupport 类

  SpecialSupport 类只解决指定编号的问题。

/**
* 只解决指定编号的问题.
*/
public class SpecialSupport extends Support{

    private int number;

    public SpecialSupport(String name, int number) {
        super(name);
        this.number = number;
    }

    @Override
    public boolean resolve(Trouble trouble) {
        if (number == trouble.getNumber()) {
            // ...
            return true;
        }
        return false;
    }
}
|| Main 类

  Main 类首先生成 Alice 至 Fred 等6个解决问题的实例。接下来,Main 类调用 setNext 方法将 Alice 至 Fred 这6个实例串联在职责链上。之后 Main 类逐个生成问题,并将它们传递给 alice,然后显示最终谁解决了该问题。

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].

  实际上,每个 Support 在调用下一个 Support 的 support 方法之前,都会先调用自身的 resolve 方法

14.3 Chain of Responsibility 模式中的登场角色

  在 Chain of Responsibility 模式中有以下登场角色。
  ◆ Handler(处理者)
  Handle 角色定义了处理请求的接口(API)。Handler 角色知道 “下一个处理者” 是谁,如果自己无法处理请求,它会将请求转给 “下一个处理者”。当然,“下一个处理者” 也是 Handler 角色。
  ◆ ConcreteHandler(具体的处理者)
  ConcreteHandler 角色是处理请求的具体角色。在示例程序中,由 NoSupport、LimitSupport、OddSupport、SpecialSupport 等各个类扮演此角色。
  ** ◆ Client(请求者)**
  Client 角色是向第一个 ConcreteHandler 角色发生请求的角色。在示例程序中,由 Main 类扮演此角色。
Chain of Responsibility 模式的类图

Chain of Responsibility 模式的类图
14.4 拓展思路的要点
|| 弱化了发出请求的人和处理请求的人之间的关系

  Chain of Responsibility 模式的最大优点就在于它弱化了发出请求的人和处理请求的人(ConcreteHandler)之间的关系。Client 角色向第一个 ConcreteHandler 角色发出请求,然后请求会在职责链中传播,直到某个 ConcreteHandler 角色处理该请求。
  如果不使用该模式,那么就必须有某个伟大的角色知道“谁该处理什么请求”,这有点类似中央集权制。而让 “发出请求的人” 知道 “谁该处理请求” 并不明智,因为如果发出请求的人不得不知道请求的人各自的责任分担,就会降低其作为可复用组件的独立性。
  在示例程序中,为了简单起见,我们让 Client 角色的 Main 类负责串联起 ConcreteHandler 的职责链。

|| 可以动态地改变职责链

  在示例程序中,问题的解决是按照从 Alice 到 Fred 的固定顺序进行处理的。但是,我们还需要考虑负责处理的各个 ConcreteHandler 角色之间的关系可能会发生变化的情况。如果使用 Chain of Responsibility 模式,通过委托推卸责任,就可以根据情况变化动态地重组责任链。
  如果不使用该模式,而是在程序中固定写明 “某个请求需要谁处理” 这样的对应关系,那么很难在程序运行中去改变请求的处理者。
  在视窗系统中,用户有时需要可以自由地在视窗中添加控件(按钮或文本输入框等)。这时,Chain of Responsibility 模式就有了用武之地。

|| 专注于自己的工作

  每个 ConcreteHandler 角色都专注于自己所负责的处理。当自己无法处理时,ConcreteHandler 角色就会干脆地对下一个处理这说一句 “交给你了”,然后将请求转出去。这样,每个 ConcreteHandler 角色就能只处理它应该负责的请求了。

|| 推卸请求会导致处理延迟吗

  使用 Chain of Responsibility 模式可以推卸请求,直至找到合适的处理请求的对象,确实提高了程序的灵活性,但是难道不会导致处理延迟吗?
  确实如此,与 “事先确定哪个对象负责什么样的处理,当接收到请求时,立即让相应的对象去处理请求” 相比,使用该模式确实导致处理请求发生了延迟。
  不过,这是一个需要权衡的问题。如果请求和处理者之间的关系是确定的,而且需要非常快的处理速度时,不是 Chain of Responsibibliy 模式会更换。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值