深入解析Java责任链设计模式:原理、实现与实战应用

责任链设计模式是行为设计模式之一。

责任链设计模式

责任链模式用于在软件设计中实现松耦合,其中客户端的请求被传递给一系列对象来处理。然后链中的对象将自行决定由谁来处理请求,以及是否需要将请求发送到链中的下一个对象。

责任链模式在JDK中的示例

让我们来看一下JDK中责任链模式的例子,然后我们将继续实现这个模式的一个现实生活例子。我们知道在try-catch代码块中可以有多个catch块。这里每个catch块都是对特定异常进行处理的处理器。因此,当try块中发生任何异常时,它被发送到第一个catch块进行处理。如果catch块无法处理它,它将将请求转发给链中的下一个对象,即下一个catch块。如果即使最后一个catch块也无法处理它,则异常被抛出到调用程序之外的链。

职责链设计模式示例

责任链模式的一个很好的例子是ATM取款机。用户输入要取出的金额,机器按照定义的货币面额(如50美元,20美元,10美元等)来取款。如果用户输入的金额不是10的倍数,就会抛出错误。我们将使用责任链模式来实现这个解决方案。链条将按照下面图片中的顺序处理请求。注意,我们可以在一个单独的程序中轻松实现这个解决方案,但是这样会增加复杂性,并且解决方案会紧密耦合。所以我们将创建一个取款系统的链条,用来取款50美元、20美元和10美元的钞票。

责任链设计模式 – 基类和接口

我们可以创建一个名为Currency的类,用于存储要发放和链式实现中使用的金额。Currency.java。

package com.Olivia.design.chainofresponsibility;

/**
 * 货币类
 */
public class Currency {

	private int amount;
	
	/**
	 * 构造函数
	 * @param amt 金额
	 */
	public Currency(int amt){
		this.amount=amt;
	}
	
	/**
	 * 获取金额
	 * @return 金额
	 */
	public int getAmount(){
		return this.amount;
	}
}

基础接口应该有一个方法来定义链中的下一个处理器和处理请求的方法。我们的ATM发放接口将如下所示。DispenseChain.java

package com.Olivia.design.chainofresponsibility;

/**
 * 取款链接口
 */
public interface DispenseChain {

	/**
	 * 设置下一个链
	 * @param nextChain 下一个链
	 */
	void setNextChain(DispenseChain nextChain);
	
	/**
	 * 分发货币
	 * @param cur 货币对象
	 */
	void dispense(Currency cur);
}

责任链模式 – 链实现

我们需要创建不同的处理器类来实现DispenseChain接口,并提供dispense方法的实现。由于我们正在开发的系统需要与三种货币纸币 – 50美元,20美元和10美元 – 进行交互,我们将创建三个具体的实现。Dollar50Dispenser.java

package com.Olivia.design.chainofresponsibility;

/**
 * 50美元分发器
 */
public class Dollar50Dispenser implements DispenseChain {

	private DispenseChain chain;
	
	@Override
	public void setNextChain(DispenseChain nextChain) {
		this.chain=nextChain;
	}

	@Override
	public void dispense(Currency cur) {
		if(cur.getAmount() >= 50){
			int num = cur.getAmount()/50;
			int remainder = cur.getAmount() % 50;
			System.out.println("发放 "+num+" 张50美元钞票");
			if(remainder !=0) this.chain.dispense(new Currency(remainder));
		}else{
			this.chain.dispense(cur);
		}
	}

}

美元20元发放机.java

package com.Olivia.design.chainofresponsibility;

/**
 * 20美元分发器
 */
public class Dollar20Dispenser implements DispenseChain{

	private DispenseChain chain;
	
	@Override
	public void setNextChain(DispenseChain nextChain) {
		this.chain=nextChain;
	}

	@Override
	public void dispense(Currency cur) {
		if(cur.getAmount() >= 20){
			int num = cur.getAmount()/20;
			int remainder = cur.getAmount() % 20;
			System.out.println("发放 "+num+" 张20美元钞票");
			if(remainder !=0) this.chain.dispense(new Currency(remainder));
		}else{
			this.chain.dispense(cur);
		}
	}

}

十美元取款机.java

package com.Olivia.design.chainofresponsibility;

/**
 * 10美元分发器
 */
public class Dollar10Dispenser implements DispenseChain {

	private DispenseChain chain;
	
	@Override
	public void setNextChain(DispenseChain nextChain) {
		this.chain=nextChain;
	}

	@Override
	public void dispense(Currency cur) {
		if(cur.getAmount() >= 10){
			int num = cur.getAmount()/10;
			int remainder = cur.getAmount() % 10;
			System.out.println("发放 "+num+" 张10美元钞票");
			if(remainder !=0) this.chain.dispense(new Currency(remainder));
		}else{
			this.chain.dispense(cur);
		}
	}

}

这里要注意的重要一点是dispense方法的实施。您会注意到每个实施都试图处理请求,并根据数量可能处理部分或全部请求。如果其中一个链条无法完全处理请求,它会将请求发送给下一个链条的处理器来处理剩余的请求。如果处理器无法处理任何内容,它只会将相同的请求转发给下一个链条。

职责链设计模式 – 创建职责链

这是一个非常重要的步骤,我们应该小心地创建链条,否则处理器可能根本不会收到任何请求。例如,在我们的实现中,如果我们将第一个处理器链保持为Dollar10Dispenser,然后是Dollar20Dispenser,那么请求将永远不会转发到第二个处理器,整个链条将变得毫无用处。这是我们处理用户请求金额的自动提款机实现,ATMDispenseChain.java。

package com.Olivia.design.chainofresponsibility;

import java.util.Scanner;

/**
 * ATM取款机责任链
 */
public class ATMDispenseChain {

	private DispenseChain c1;

	public ATMDispenseChain() {
		// 初始化链条
		this.c1 = new Dollar50Dispenser();
		DispenseChain c2 = new Dollar20Dispenser();
		DispenseChain c3 = new Dollar10Dispenser();

		// 设置责任链
		c1.setNextChain(c2);
		c2.setNextChain(c3);
	}

	public static void main(String[] args) {
		ATMDispenseChain atmDispenser = new ATMDispenseChain();
		while (true) {
			int amount = 0;
			System.out.println("请输入要取款的金额");
			Scanner input = new Scanner(System.in);
			amount = input.nextInt();
			if (amount % 10 != 0) {
				System.out.println("金额应该是10的倍数。");
				return;
			}
			// 处理请求
			atmDispenser.c1.dispense(new Currency(amount));
		}

	}

}

当我们运行以上应用程序时,我们会得到如下的输出。

请输入要取款的金额
530
发放 10 张50美元钞票
发放 1 张20美元钞票
发放 1 张10美元钞票
请输入要取款的金额
100
发放 2 张50美元钞票
请输入要取款的金额
120
发放 2 张50美元钞票
发放 1 张20美元钞票
请输入要取款的金额
15
金额应该是10的倍数。

职责链设计模式类图

我们自动取款机链式责任设计模式实现的示例如下图所示。

责任链设计模式的重要要点

  • 客户端不知道链中的哪一部分将处理请求,它会将请求发送到链中的第一个对象。例如,在我们的程序中,ATMDispenseChain不知道是谁在处理分配输入金额的请求。
  • 链中的每个对象都有自己的实现来处理请求,可以是全部、部分处理,或者将其发送到链中的下一个对象。
  • 链中的每个对象都应该有对链中下一个对象的引用,以便将请求转发给它,这是通过Java组合实现的。
  • 仔细创建链非常重要,否则可能出现请求永远不会转发到特定处理器的情况,或者链中没有能够处理请求的对象。在我的实现中,我添加了对用户输入金额的检查,以确保它被所有处理器完全处理,但我们可能不检查它,如果请求到达最后一个对象并且链中没有其他对象可以转发请求,则抛出异常。这是一个设计决策。
  • 责任链设计模式有助于实现松耦合,但它的代价是有很多实现类,如果所有实现中的大部分代码都是通用的,则会出现维护问题。

在JDK中,责任链模式的例子

  • java.util.logging.Logger#log()
  • javax.servlet.Filter#doFilter()

这就是责任链设计模式的全部,希望你喜欢并且它能够帮助你清楚地理解这个设计模式。

bannerAds