责任链模式(Chain-of-responsibility pattern)

简介

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

以下的日志类(logging)例子演示了该模式。 每一个logging handler首先决定是否需要在该层做处理,然后将控制传递到下一个logging handler。程序的输出是:

  1. Writing to debug output: Entering function y.
  2. Writing to debug output: Step1 completed.
  3. Sending via e-mail: Step1 completed.
  4. Writing to debug output: An error has occurred.
  5. Sending via e-mail: An error has occurred.
  6. Writing to stderr: An error has occurred.

注意:该例子不是日志类的推荐实现方式。

同时,需要注意的是,通常在责任链模式的实现中,如果在某一层已经处理了这个logger,那么这个logger就不会传递下去。在我们这个例子中,消息会一直传递到最底层不管它是否已经被处理。

  1. import java.util.*;
  2. abstract class Logger
  3. {
  4. public static int ERR = 3;
  5. public static int NOTICE = 5;
  6. public static int DEBUG = 7;
  7. protected int mask;
  8. // The next element in the chain of responsibility
  9. protected Logger next;
  10. public Logger setNext( Logger l)
  11. {
  12. next = l;
  13. return this;
  14. }
  15. public final void message( String msg, int priority )
  16. {
  17. if ( priority <= mask )
  18. {
  19. writeMessage( msg );
  20. if ( next != null )
  21. {
  22. next.message( msg, priority );
  23. }
  24. }
  25. }
  26. protected abstract void writeMessage( String msg );
  27. }
  28. class StdoutLogger extends Logger
  29. {
  30. public StdoutLogger( int mask ) { this.mask = mask; }
  31. protected void writeMessage( String msg )
  32. {
  33. System.out.println( "Writting to stdout: " + msg );
  34. }
  35. }
  36. class EmailLogger extends Logger
  37. {
  38. public EmailLogger( int mask ) { this.mask = mask; }
  39. protected void writeMessage( String msg )
  40. {
  41. System.out.println( "Sending via email: " + msg );
  42. }
  43. }
  44. class StderrLogger extends Logger
  45. {
  46. public StderrLogger( int mask ) { this.mask = mask; }
  47. protected void writeMessage( String msg )
  48. {
  49. System.out.println( "Sending to stderr: " + msg );
  50. }
  51. }
  52. public class ChainOfResponsibilityExample
  53. {
  54. public static void main( String[] args )
  55. {
  56. // Build the chain of responsibility
  57. Logger l = new StdoutLogger( Logger.DEBUG).setNext(
  58. new EmailLogger( Logger.NOTICE ).setNext(
  59. new StderrLogger( Logger.ERR ) ) );
  60. // Handled by StdoutLogger
  61. l.message( "Entering function y.", Logger.DEBUG );
  62. // Handled by StdoutLogger and EmailLogger
  63. l.message( "Step1 completed.", Logger.NOTICE );
  64. // Handled by all three loggers
  65. l.message( "An error has occurred.", Logger.ERR );
  66. }
  67. }

加薪代码初步

  1. //申请
  2. class Request
  3. {
  4. //申请类别
  5. private string requestType;
  6. public string RequestType
  7. {
  8. get {return requestType;}
  9. set {requestType = value;}
  10. }
  11. //申请内容
  12. private string requestContent;
  13. public string RequestContent
  14. {
  15. get {return RequestContent;}
  16. set {RequestContent = value;}
  17. }
  18. //数量
  19. private int number;
  20. public int Number
  21. {
  22. get {return number;}
  23. set {number = value;}
  24. }
  25. }
  1. //管理者
  2. class Manager
  3. {
  4. protected string name;
  5. public Manager(string name)
  6. {
  7. this.name = name;
  8. }
  9. //得到结果
  10. public void GetResult(string managerLevel, Request request)
  11. {
  12. if (request.RequestType=="经理") {
  13. if (request.RequestType == "请假" && request.Number <= 2)
  14. {
  15. Console.WriteLine("被批准");
  16. } else
  17. {
  18. Console.WriteLine("我无权处理");
  19. }
  20. } else if (request.RequestType=="总监") {
  21. if (request.RequestType == "请假" && request.Number <= 5)
  22. {
  23. Console.WriteLine("被批准");
  24. } else
  25. {
  26. Console.WriteLine("我无权处理");
  27. }
  28. } else if (request.RequestType=="总经理") {
  29. if (request.RequestType == "请假“) {
  30. Console.WriteLine("被批准");
  31. } else if (request.RequestType == "加薪" && request.Number <= 500) {
  32. Console.WriteLine("被批准");
  33. } else if (request.RequestType == "加薪" && request.Number > 500) {
  34. Console.WriteLine("再说");
  35. }
  36. }
  37. }
  38. }

客户端代码如下

  1. static void Main(string[] args)
  2. {
  3. Manager jinli = new Manager("金利");
  4. Manager zongjian = new Manager("宗剑");
  5. Manager zhongjingli = new Manager("钟精励");
  6. Request request = new Reuqest();
  7. request.RequestType = "加薪";
  8. request.RequestContent = "小菜请求加薪";
  9. request.Number = 1000;
  10. Request request2 = new Reuqest();
  11. request2.RequestType = "请假";
  12. request2.RequestContent = "小菜请假";
  13. request2.Number = 3;
  14. jinli.GetResult("经理",request2);
  15. zongjian.GetResult("总监",request2);
  16. zhongjingli.GetResult("总经理",request2);
  17. Console.Read();
  18. }

代码评判:’管理者’类里面的’结果’方法比较长,加上有太多的分支判断,这种设计很不好。而且会不会增加其它的管理类别,比如说项目经理,部门经理,那就意味者需要去更改这个类,这个类承担了太多的责任,这违背了哪些设计原则?

类有太多的责任,这违背了单一职责原则,增加新的管理类别,需要修改这个类,违背了开放-封闭原则。

子类化加多太改善。

职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着个链传递请求,直到有一个对象处理为止。

加薪代码重构

  1. //管理者
  2. abstract class Manager
  3. {
  4. protected Manager superior;
  5. public Manager(string name)
  6. {
  7. this.name = name;
  8. }
  9. //设置管理者的上级
  10. public void setSuperior(Manager superior)
  11. {
  12. this.superior = superior;
  13. }
  14. }

经理类就可以去继承这个’管理者’类,只需重写’申请要求’的方法就可以了。

  1. class CommonManager : Manager
  2. {
  3. public CommonManager(string name) :base(name)
  4. {}
  5. public override void RequestApplications(Request request)
  6. {
  7. if (request.RequestType == "请假" && request.Number <= 2)
  8. {
  9. Console.WriteLine("被批准");
  10. } else
  11. {
  12. if (superior != null) superior.RequestApplications(request);
  13. }
  14. }
  15. }

总监类同样继承’管理者类’。

  1. //总监
  2. class Majordomo :Manager
  3. {
  4. public Major(string name): base(name)
  5. {}
  6. public override void RequestApplications(Request request)
  7. {
  8. if (request.RequestType == "请假" && request.Number <= 2)
  9. {
  10. Console.WriteLine("被批准");
  11. } else
  12. {
  13. if (superior != null) superior.RequestApplications(request);
  14. }
  15. }
  16. }

总经理类的权限就是全部都需要处理。

  1. //总经理
  2. class GeneralManager :Manager
  3. {
  4. public GeneralManager(string name): base(name)
  5. {}
  6. public override void RequestApplications(Request request)
  7. {
  8. if (request.RequestType == "请假")
  9. {
  10. Console.WriteLine("被批准");
  11. } else if (request.RequestType == "加薪" && request.Number <= 500)
  12. {
  13. Console.WriteLine("被批准");
  14. } else if (request.RequestType == "加薪" && request.Number > 500)
  15. {
  16. Console.WriteLine("再说吧");
  17. }
  18. }
  19. }

由于我们把你原来的一个’管理者’类改成了一个抽象类和三个具体类,此时类之间的灵活性就大大的增加了,如果我们需要扩展新的管理类别,只需要增加子类就可以。当然,还有一个关键,那就是客户端如何编写。

  1. static void Main(string[] args)
  2. {
  3. CommonManager jinli = new CommonManger("金利");
  4. Majordomo zongjian = new Majordomo("宗建");
  5. GeneralManager zhongjingli = newGeneralManager("钟精利");
  6. //设置上级
  7. jinli.SetSuperior(zongjian);
  8. zongjian.SetSuperior(zhongjingli);
  9. Request request = new Request();
  10. request.RequestType = "请假";
  11. request.RequestContent = "小菜请假";
  12. request.Number = 1;
  13. //客户端的申请都是有‘经理’发起,但是实际谁来决策都是由具体管理类处理,客户端不知道
  14. jinli.requestApplications(request);
  15. Request request1 = new Request();
  16. request1.RequestType = "请假";
  17. request1.RequestContent = "小菜请假";
  18. request1.Number = 4;
  19. jinli.requestApplications(request1);
  20. Request request2 = new Request();
  21. request2.RequestType = "请求加薪";
  22. request2.RequestContent = "小菜请加薪";
  23. request2.Number = 400;
  24. jinli.requestApplications(request2);
  25. Console.Read();
  26. }

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

门框夹核桃

职责链模式想要做到的事情其实就是把多个函数链起来调用。

该模式提出的时候FP并不如今日盛行,其作者选用类来包装需要被链接的多个函数,这无可厚非。

无论是class,还是function,都是为程序员提供抽象的手段。当我们想要链接的东西就是多个function,选择直接用function而非class就会显得更加自然,也更加轻量且合适。

  1. object Loggers {
  2. val ERR = 3
  3. val NOTICE = 5
  4. val DEBUG = 7
  5. case class Event(message: String, priority: Int)
  6. type Logger = Event => Event
  7. def stdOutLogger(mask: Int): Logger = event => handleEvent(event, mask) {
  8. println(s"Writing to stdout: ${event.message}")
  9. }
  10. def emailLogger(mask: Int): Logger = event => handleEvent(event, mask) {
  11. println(s"Sending via e-mail: ${event.message}")
  12. }
  13. def stdErrLogger(mask: Int): Logger = event => handleEvent(event, mask) {
  14. System.err.println(s"Sending to stderr: ${event.message}")
  15. }
  16. private def handleEvent(event: Event, mask: Int)(handler: => Unit) = {
  17. if (event.priority <= mask) handler
  18. event
  19. }
  20. }

三个log的的等级ERR,NOTICE和DEBUG和之前Java的实现是一样的。

一个case class Event,用来包裹需要被log的事件。

type Logger则是声明了一个函数签名,凡是符合这个签名的函数都可以作为logger被使用。

然后便是三个函数实现,它们将mask通过闭包封进函数内。这三个函数共同依赖一个私有handleEvent函数,其作用和Java代码中的message类似,判断mask和正在发生的事件之间优先级大小关系,并以此决定当前logger是否需要处理该事件。

哎?等一下,这个是职责链模式啊,那个啥,链在哪儿呢?

  1. object ChainRunner {
  2. import chain.Loggers._
  3. def main(args: Array[String]) {
  4. val chain = stdOutLogger(DEBUG) andThen emailLogger(NOTICE) andThen stdErrLogger(ERR)
  5. chain(Event("Entering function y.", DEBUG))
  6. chain(Event("Step1 completed.", NOTICE))
  7. chain(Event("An error has occurred.", ERR))
  8. }
  9. }

以上代码中的andThen就可以把三个logger链在一起。