[设计模式] 设计模式之责任链模式【3】

1 概述

1.1 模式定义

  • 责任链模式(Chain of Responsibility, CoR、Chain of Command、职责链模式、命令链):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
  • 使多个对象都有机会处理请求
  • 请求在这个链上传递,直到链上的某一个对象决定处理此请求。
  • 发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织分配责任
  • 模式的目标

允许将请求沿着处理者链路传递,直到请求被处理为止。

  • 主要解决的问题

解耦请求的发送者接收者,使多个对象都有可能接收请求,而发送者不需要知道哪个对象会处理它。

  • 责任链模式是一种行为设计模式:允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

1.2 主要角色

责任链模式涉及到的角色如下所示:

  • 抽象处理者(Handler)角色:定义出一个处理请求的接口(如 handleRequest)。

如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。
这个角色通常由一个Java抽象类或者Java接口实现。
Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

  • 具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。

由于具体处理者持有对下家的引用。
因此,如果需要,具体处理者可以访问下家。

  • 客户端Client):

创建处理者对象,并将它们连接成一条责任链
通常,客户端只需要将请求发送给责任链的第一个处理者,无需关心请求的具体处理过程。

1.3 实现方式

  • 定义处理者接口:所有处理者必须实现同一个接口。
  • Handler接口:定义一个方法用于处理请求。
  • 创建具体处理者:实现接口的具体类,包含请求处理逻辑和指向链中下一个处理者的引用。
  • ConcreteHandler类:实现Handler接口,包含请求处理逻辑和对下一个处理者的引用

1.4 适用场景

  • 当有多个对象可以处理请求,且具体由哪个对象处理由运行时决定时。

  • 当需要向多个对象中的一个提交请求,而不想明确指定接收者时。

1.5 模式特点

优点

  • 降低耦合度:发送者和接收者之间解耦。
  • 简化对象:对象不需要知道链的结构。
  • 灵活性:通过改变链的成员或顺序,动态地新增或删除责任。
  • 易于扩展:增加新的请求处理类很方便。

缺点

  • 请求未被处理:不能保证请求一定会被链中的某个处理者接收。
  • 性能影响:可能影响系统性能,且调试困难,可能导致循环调用。
  • 难以观察:运行时特征不明显,可能妨碍除错。

1.6 使用建议

  • 在处理请求时,如果有多个潜在的处理者,考虑使用责任链模式。
  • 确保链中的每个处理者都明确知道如何传递请求到链的下一个环节。

2 现实场景与案例分析

2.1 击鼓传花

  • 击鼓传花:游戏中的传递行为,直到音乐停止。

2.2 事件冒泡

  • 事件冒泡:在JavaScript中,事件从最具体的元素开始,逐级向上传播。

2.3 Web服务器对用户请求的链式处理

  • 在Java Web开发中,责任链模式有广泛应用,如过滤器链、拦截器等。
  • 如:Apache Tomcat处理字符编码,Struts2的拦截器,及Servlet的Filter。

2.4 生产线

  • 问题背景
    倘若一个系统中有一系列零散的功能节点,它们都负责处理相关的业务,但处理方式又各不相同。
    这时客户面对这么一大堆功能节点可能无从下手,根本不知道选择哪个功能节点去提交请求,返回的结果也许只是个半成品,还得再次提交给下一个功能节点,处理过程相当烦琐。
    虽然从某种角度看,每个功能节点均承担各自的义务,分工明确、各司其职,但从外部来看则显得毫无组织,团队犹如一盘散沙。
    所以为了更高效、更完整地解决客户的问题,各节点一定要发扬团队精神,利用责任链模式组织起来,形成一个有序、有效的业务处理集群,为客户提供更方便、更快捷的服务。

  • 汽车生产线的制造流程

以最简单的责任链举例,汽车生产线的制造流程就使用了这种模式:

  • 首先,我们进行劳动分工,将汽车零件的安装工作拆分并分配给各安装节点,责任明确划分;
  • 然后,架构生产线,将安装节点组织起来,首尾相接,规划操作流程;
  • 最终,通过生产线的传递,汽车便从零件到成品得以量产,生产效率大大提升。

我们将汽车生产线从左至右分为3个功能节点,其中:

  • A节点负责组装车架、安装车轮
  • B节点负责安装发动机油箱传动轴【内部机件】
  • C节点进行组装外壳、喷漆等美容操作

这样将产品逐级传递,每经过一个节点就完成一部分工作,最终完成产品交付。

2.5 OA系统的报销审批流程

  • 问题背景

生产线的例子其实相对机械、简单,我们来看一个带有一些逻辑的责任链:报销审批流程。

  • 报销审批流程

公司为了更高效、安全规范地把控审核工作,通常会将整个审批工作过程按负责人或者工作职责进行拆分,并组织好各个环节中的逻辑关系及走向,最终形成标准化的审批流程,如图所示。

  • 审批流程需要依次通过财务专员、财务经理、财务总监的审批。

  • 如果申请金额在审批人的审批职权范围内,则:审批通过并终止流程;反之,则:会升级至更高层级的上级去继续审批;

  • 直至最终的财务总监,如果仍旧超出财务总监的审批金额,则:驳回申请,流程终止。

  • 需求
    假设我们正在开发一款办公系统,系统有个模块是审批,起初审批只有一级审批,直接提交,主管进行审批即可。
    随着时间推移,人员越来越多,公司的组织架构也在扩大。审批的请求自然也多了。
    于是要将审批按照金额进行划分审核人员,超过金额数就上级进行审批,比如:

  • 100元以内 => 财务专员审批即可,否则将由财务组长审批

  • 1000以内 => 财务组长审批即可,否则将由财务主管审批

  • 10000以内 => 财务主管审批即可,否则不通过(或者交由老板审批)

2.6 日志器

Logger

import java.util.*;

public abstract class Logger {
	public static int ERR = 3;
	public static int NOTICE = 5;
	public static int DEBUG = 7;
	protected int mask;
	
	// The next element in the chain of responsibility
	protected Logger next;
	
	public Logger setNext( Logger l) {
        next = l;
        return this;    
	}
    
	public final void message( String msg, int priority )    {
        if ( priority <= mask ) {
			writeMessage( msg );
			if ( next != null ) {
                next.message( msg, priority );            
			}
		}
	}    
    
	protected abstract void writeMessage( String msg );
}

StdoutLogger

public class StdoutLogger extends Logger {
	public StdoutLogger( int mask ) { 
		this.mask = mask; 
	}
	
	protected void writeMessage( String msg )    {
		System.out.println( "Writting to stdout: " + msg );
	}
}

EmailLogger

public class EmailLogger extends Logger {
	public EmailLogger( int mask ) { this.mask = mask; }
	protected void writeMessage( String msg )    {
		System.out.println( "Sending via email: " + msg );    
	}
}

StderrLogger

public class StderrLogger extends Logger {
	public StderrLogger( int mask ) { 
		this.mask = mask; 
	}    
	
	protected void writeMessage( String msg ) {
		System.out.println( "Sending to stderr: " + msg );
	}
}

[client] ChainOfResponsibilityExample

public class ChainOfResponsibilityExample {    
	public static void main( String[] args ){
	// Build the chain of responsibility
	Logger l = new StdoutLogger( Logger.DEBUG).setNext(
		new EmailLogger( Logger.NOTICE ).setNext(
			new StderrLogger( Logger.ERR )
		) 
	);
	// Handled by StdoutLogger
	l.message( "Entering function y.", Logger.DEBUG );
	// Handled by StdoutLogger and EmailLogger
	l.message( "Step1 completed.", Logger.NOTICE );
	// Handled by all three loggers
	l.message( "An error has occurred.", Logger.ERR );    
	}
}

2.7 日志器(2) 【推荐】

例如:我们创建抽象类 AbstractLogger,带有详细的日志记录级别。
然后,我们创建三种类型的记录器,都扩展了 AbstractLogger
每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。

AbstractLogger : 创建抽象的记录器类

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;
 
   protected int level;
 
   //责任链中的下一个元素
   protected AbstractLogger nextLogger;
 
   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }
 
   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }
 
   abstract protected void write(String message);
   
}

ConsoleLogger : 创建扩展了该记录器类的实体类1

public class ConsoleLogger extends AbstractLogger {
 
   public ConsoleLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("Standard Console::Logger: " + message);
   }
}

ErrorLogger : 创建扩展了该记录器类的实体类2

public class ErrorLogger extends AbstractLogger {
 
   public ErrorLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("Error Console::Logger: " + message);
   }
}

FileLogger : 创建扩展了该记录器类的实体类3

public class FileLogger extends AbstractLogger {
 
   public FileLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("File::Logger: " + message);
   }
}

Client/ChainPatternDemo

  • 创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。
public class ChainPatternDemo {
   
   private static AbstractLogger getChainOfLoggers(){
 
      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
 
      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);
 
      return errorLogger;  
   }
 
   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();
 
      loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
 
      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is a debug level information.");
 
      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

out:

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

X 参考文献

posted @ 2023-03-14 12:55  千千寰宇  阅读(93)  评论(0)    收藏  举报