设计模式–责任链 Chain of Responsibility(附源码)

设计模式–责任链

本文为翻译自 https://www.journaldev.com/1617/chain-of-responsibility-design-pattern-in-java ,为符合国人阅读习惯,对一部分内容做了调整

责任链模式在软件设计中被用来解耦,即将客户端发出的请求,传递给若干个对象,这些对象形成一条链,即为责任链,请求沿着这条链依次向下传递,链上的对象可以处理请求,也可以将请求转发给下一个对象。为了更加形象的解释责任链,不妨把上述的对象称为 节点

下载演示代码 代码,导入 eclipse 中直接运行

责任链模式在JDK中的应用

   来看一个简单的例子,在java中,try-catche 块中可以有多个 catch,当被 try 包裹的代码块中出现异常时,异常被发送给第一个 catch 块,如果这个 catch 块处理不了,就传递给下一个 catch 块,每一个 catch块 处理相应的异常,如果直到最后一个 catch 块也处理不了这个异常,异常将被抛出。

try{
//do something
}
catch (IOException ex) {

catch (SQLException ex) {
    
}catch (Exception ex) {
   
}

责任链模式的具体例子

   假设ATM中分别有面值为 50元、20元和10元的钞票,我们去ATM机取钱的时候,先输入想要取出的金额,机器根据输入的金额和钞票的面值,计算出应该弹出多少张不同面值的纸币。例如输入金额为60元,应该弹出一张50块的和一张10块的钞票(而不是弹出6张10块的钞票。

下面我们用责任链模式来处理这个问题。当然了,不用责任链模式也能解决这个问题,但是使用责任链可以降低代码之间的耦合,降低复杂度。

处理流程如下图所示,C1,C2,C3为责任链上的三个节点。

基础类和接口

Currency.java

//Currency 类存放了用户想要取出的金额
package com;
public class Currency {

	private int amount;
	
	public Currency(int amt){
		this.amount=amt;
	}
	
	public int getAmount(){
		return this.amount;
	}
}

责任链上的每一个节点都必须做两件事情:
1.处理请求、2.指明下一个要传递的节点。
因此定义接口类如下

DispenseChain.java

package com;

public interface DispenseChain {
	
	void setNextChain(DispenseChain nextChain);//处理请求
	
	void dispense(Currency cur);//指明下一个要传递的节点
}

责任链上的节点

在本例中,我的货币有三种面值——50、20、10。所以我们创建三个节点,每个节点都实现 DispenseChain 接口。

Dollar50Dispenser.java

package com;

public class Dollar50Dispenser implements DispenseChain {

	private DispenseChain chain;

	public void setNextChain(DispenseChain nextChain) {
		this.chain = nextChain;

	}

	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);
		}
	}

}

Dollar20Dispenser.java

package com;

public class Dollar20Dispenser implements DispenseChain {
	private DispenseChain chain;
	public void setNextChain(DispenseChain nextChain) {
		this.chain=nextChain;

	}

	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);
		}

	}

}

Dollar10Dispenser.java

package com;

public class Dollar10Dispenser implements DispenseChain {
	private DispenseChain chain;

	public void setNextChain(DispenseChain nextChain) {
		this.chain = nextChain;

	}

	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 方法,dispense 方法根据用户输入的金额,计算弹出多少张纸币。

责任链的创建

创建责任链的时候要注意节点的顺序,如果节点顺序不对,可能请求永远无法到达到某个节点,例如我们把c1节点和c3节点对调,那么请求永远不会来到c1节点。那这个责任链就没用了。

我们运行下面这个类

ATMDispenseChain.java

package com;
import java.util.Scanner;	

import com.sun.corba.se.spi.orbutil.fsm.Input;
public class ATMDispenseChain {
	private DispenseChain c1;

	public ATMDispenseChain() {
		
		this.c1 = new Dollar50Dispenser();
		DispenseChain c2 = new Dollar20Dispenser();
		DispenseChain c3 = new Dollar10Dispenser();

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

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

运行截图如下
在这里插入图片描述

总体设计的类图如下所示

责任链模式的重点:

  • 客户把请求发给责任链上的第一个节点,但是它并不知道责任链上的哪个节点处理了请求。在上述ATM机的例子中,ATMDispenseChain 将请求发给 c1(Dollar50Dispenser),但是它并不知道后续的哪个节点都干了什么。

  • 责任链上的每个节点都处理属于自己的部分,然后将请求发送给下一个节点,或者只是将未处理的部分发送给下一节点。

  • 在责任链中要小心的设计节点,如果节点设计的不好,可能无法处理某些请求,如果节点顺序不对,请求可能永远无法到达某些节点。

  • 责任链这种设计模式可以大幅度减少代码之间的耦合,但是同时也带来一些问题,使用责任链需要你创建许多个对象(节点),因为这些对象可能有一大部分代码都是类似的,所以如果节点没有设计好,出现问题的时候就需要维护很多个对象,给开发者带来很多麻烦。在实际应用中还请自行取舍。

JDK中其他用到责任链的地方

  • java.util.logging.Logger#log()
  • javax.servlet.Filter#doFilter()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值