设计模式(十四)Chain of Responsibility模式
Chain of Responsibility模式就是当外部请求程序进行某个处理,但程序暂时无法直接决定由哪个对象负责处理时,就需要推卸责任。也就是说,当一个人被要求做什么事时,如果他可以做就自己做,如果不能做就转给下一个人,以此类推。
下面是示例程序的类图。
下面是示例程序代码。
1 package bigjunoba.bjtu.handler; 2 3 public class Trouble { 4 5 private int number; 6 7 public Trouble(int number) { 8 this.number = number; 9 } 10 11 public int getNumber() { 12 return number; 13 } 14 15 public String toString() { 16 return "[Trouble " + number + "]"; 17 } 18 19 }
Trouble类是表示发生的问题的类,number作为问题编号,通过getNumber方法得到问题编号。
1 package bigjunoba.bjtu.handler; 2 3 public abstract class Support { 4 5 private String name; 6 private Support next; 7 8 public Support(String name) { 9 super(); 10 this.name = name; 11 } 12 13 public Support setNext(Support next) { 14 this.next = next; 15 return next; 16 } 17 18 public final void support(Trouble trouble) { 19 if (resolve(trouble)) { 20 done(trouble); 21 } else if (next != null) { 22 next.support(trouble); 23 }else { 24 fail(trouble); 25 } 26 } 27 28 public String toString() { 29 return "[" + name + "]"; 30 } 31 32 protected abstract boolean resolve(Trouble trouble); 33 34 protected void done(Trouble trouble) { 35 System.out.println(trouble + " is resolved by " + this + "." ); 36 } 37 38 protected void fail(Trouble trouble) { 39 System.out.println(trouble + " cannot be resolved."); 40 } 41 42 }
Support类是用来解决问题的抽象类,它是职责链上的对象。next字段保存了要推卸给的对象,即Support类的实例,可以通过setNext方法设定该对象。resolve方法如果返回true,则表示问题已经被处理,如果返回false,则表示问题还没有被处理。support方法调用resolve方法,如果解决了问题,那么就调用done方法,如果否则将问题交给下一个对象,如果到最后一个对象仍然没有人处理,那么就调用fail方法。这里注意的是support方法调用抽象resolve方法,这属于Template Method模式。
1 package bigjunoba.bjtu.concretehandler; 2 3 import bigjunoba.bjtu.handler.Support; 4 import bigjunoba.bjtu.handler.Trouble; 5 6 public class NoSupport extends Support{ 7 8 public NoSupport(String name) { 9 super(name); 10 } 11 12 @Override 13 protected boolean resolve(Trouble trouble) { 14 return false; 15 } 16 17 }
1 package bigjunoba.bjtu.concretehandler; 2 3 import bigjunoba.bjtu.handler.Support; 4 import bigjunoba.bjtu.handler.Trouble; 5 6 public class OddSupport extends Support { 7 8 public OddSupport(String name) { 9 super(name); 10 } 11 12 @Override 13 protected boolean resolve(Trouble trouble) { 14 if (trouble.getNumber() % 2 == 1) { 15 return true; 16 } else { 17 return false; 18 } 19 } 20 21 }
package bigjunoba.bjtu.concretehandler; import bigjunoba.bjtu.handler.Support; import bigjunoba.bjtu.handler.Trouble; public class SpecilaSupport extends Support{ private int number; public SpecilaSupport(String name, int number) { super(name); this.number = number; } @Override protected boolean resolve(Trouble trouble) { if (trouble.getNumber() == number) { return true; } else { return false; } } }
1 package bigjunoba.bjtu.concretehandler; 2 3 import bigjunoba.bjtu.handler.Support; 4 import bigjunoba.bjtu.handler.Trouble; 5 6 public class LimitSupport extends Support{ 7 8 private int limit; 9 10 public LimitSupport(String name, int limit) { 11 super(name); 12 this.limit = limit; 13 } 14 15 @Override 16 protected boolean resolve(Trouble trouble) { 17 if (trouble.getNumber() < limit) { 18 return true; 19 } else { 20 return false; 21 } 22 } 23 24 }
这四种类Support类的子类不难理解。NoSupport类永远不解决问题;LimitSupport类只解决编号小于limit值的问题;OddSupport类只解决奇数编号问题;SpecialSupport类只解决特定编号的问题。
1 package bigjunoba.bjtu.test; 2 3 import bigjunoba.bjtu.concretehandler.LimitSupport; 4 import bigjunoba.bjtu.concretehandler.NoSupport; 5 import bigjunoba.bjtu.concretehandler.OddSupport; 6 import bigjunoba.bjtu.concretehandler.SpecilaSupport; 7 import bigjunoba.bjtu.handler.Support; 8 import bigjunoba.bjtu.handler.Trouble; 9 10 public class Main { 11 12 public static void main(String[] args) { 13 Support lian1 = new NoSupport("LianOne"); 14 Support lian2 = new LimitSupport("LianTwo", 100); 15 Support lian3 = new SpecilaSupport("LianThree", 429); 16 Support lian4 = new LimitSupport("LianFour", 200); 17 Support lian5 = new OddSupport("LianFive"); 18 Support lian6 = new LimitSupport("LianSix", 300); 19 //形成职责链 20 lian1.setNext(lian2).setNext(lian3).setNext(lian4).setNext(lian5).setNext(lian6); 21 //制造各种问题 22 for (int i = 0; i < 500; i += 33) { 23 lian1.support(new Trouble(i)); 24 } 25 } 26 27 }
Main类首先生成了6个解决问题的实例,虽然都是Support类型的,但实际上由于Support类是抽象类,因此这些实例分别是四个不同的子类的实例。然后形成了职责链,接着制造问题进行处理。
[Trouble 0] is resolved by [LianTwo]. [Trouble 33] is resolved by [LianTwo]. [Trouble 66] is resolved by [LianTwo]. [Trouble 99] is resolved by [LianTwo]. [Trouble 132] is resolved by [LianFour]. [Trouble 165] is resolved by [LianFour]. [Trouble 198] is resolved by [LianFour]. [Trouble 231] is resolved by [LianFive]. [Trouble 264] is resolved by [LianSix]. [Trouble 297] is resolved by [LianFive]. [Trouble 330] cannot be resolved. [Trouble 363] is resolved by [LianFive]. [Trouble 396] cannot be resolved. [Trouble 429] is resolved by [LianThree]. [Trouble 462] cannot be resolved. [Trouble 495] is resolved by [LianFive].
根据输出结果来举一个例子具体分析一下这个过程。假设问题编号是429,那么可以这样来分析,首先创建了一个Trouble实例,并且把问题编号number设置为429,然后这个429问题实例传递给support方法,并用lian1来调用这个方法。主要分析的是调用方法的过程:先调用NoSupport类的resolve方法,结果返回false,然后next字段保存的是lian2,不为空,因此要执行lian2的support方法...以此类推,最后发现lian1,2都解决不了,最后交给了3,3可以解决,程序结束。这里的思想是用到了递归。
有一个时序图也可以用来帮助理解。
下面是Chain of Responsibility模式的类图。
这里对类图不做过多解释,通过示例程序对这一模式理解应该很到位。