一、击鼓传花
击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客一次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。开始击鼓时,花束就开始依次传递,鼓声一落,如果花束在某人手中,则该人就得饮酒。
比如说,贾母、贾赦、贾政、贾宝玉和贾环是五个参加击鼓传花游戏的传花者,他们组成一个环链。击鼓者将花传给贾母,开始传花游戏。花由贾母传给贾赦,贾赦传给贾政,贾政传给贾宝玉,贾宝玉传给贾环,贾环再传给贾母,由此往复,如下图所示。
击鼓传花便是一种典型的责任链模式。
二、什么是责任链模式
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
三、责任链模式的结构
● 抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
● 具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
四、责任链模式的实例
我为更清楚的看出责任链模式的结构,我们先来看一看最简答的代码实现。
package com.designpattern.pre1;
/**
* 抽象处理者,定义处理者的接口
*
* @author 98583
*
*/
public abstract class Handler {
/**
* 下一个处理者
*/
protected Handler successor;
/**
* 每个处理者的处理方法
*/
public abstract void handleRequest();
/**
* 设置下一个处理者
*
* @param successor
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 获取下一个处理者
*
* @return
*/
public Handler getSuccessor() {
return successor;
}
}
package com.designpattern.pre1;
/**
* 具体处理者
*
* @author 98583
*
*/
public class ConcreteHandler extends Handler {
/**
* 处理方法
*/
public void handleRequest() {
/**
* 如果有下一个处理者就交给下一个处理者,但是实际情况中应该判断这个处理者能否处理这个问题,不能处理才传给下一个处理者
*/
if (getSuccessor() != null) {
System.out.println("The request is passed to " + getSuccessor());
getSuccessor().handleRequest();
}// 在这个处理者中处理
else {
System.out.println("The request is handled here.");
}
}
}
package com.designpattern.pre1;
public class Client {
static private Handler handler1, handler2;
public static void main(String[] args) {
/**
* 定义两个处理者
*/
handler1 = new ConcreteHandler();
handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
handler1.handleRequest();
}
}
现在了解了责任链模式的基本结构,我们可以来实现上边的红楼梦中的击鼓传花的故事了。
package com.designpattern.chainofresp;
/**
* 相当于抽象的Handler
* @author 98583
*
*/
abstract class Player {
abstract public void handle(int i);
/**
* 下一位处理者
*/
private Player successor;
public Player() {
successor = null;
}
/**
* 设置下一个处理者
* @param aSuccessor
*/
protected void setSuccessor(Player aSuccessor) {
successor = aSuccessor;
}
/**
* 传给下一个处理者,这个方法本不应该出现在这,因为每个处理者中都有这个方法,所以就放到父类中来了
* @param index
*/
public void next(int index) {
if (successor != null) {
successor.handle(index);
} else {
System.out.println("Program terminated.");
}
}
}
package com.designpattern.chainofresp;
/**
* 贾母的类相当于ConcreteHandler
* @author 98583
*
*/
class JiaMu extends Player {
public JiaMu(Player aSuccessor) {
this.setSuccessor(aSuccessor);
}
/**
* 这个处理和的处理方法
*/
public void handle(int i) {
if (i == 1) {
System.out.println("Jia Mu gotta drink!");
} else {
System.out.println("Jia Mu passed!");
/**
* 传给下一个处理者
*/
next(i);
}
}
}
其他人的类与贾母的类相似,不再贴出,最后附上源码。
package com.designpattern.chainofresp;
/**
* 击鼓者,相当于客户端
* @author 98583
*
*/
public class DrumBeater
{
private static Player player;
static public void main(String[] args)
{
JiaMu jiaMu = new JiaMu(null);
jiaMu.setSuccessor( new JiaShe (
new JiaZheng(
new JiaBaoYu(
new JiaHuan( jiaMu ) ) ) ) );
player = jiaMu;
player.handle(4);
}
}
五、再来一个示例
有一个银行的借款系统可以帮助用户借款,负责该系统的有职员,组长还有经理,他们的等级由低到高,并且他们能够允许借款的额度也是从低到高的,分别是:
- 职员最高可以批准 5000 元的借款额度
- 组长最高可以批准 20000 元的借款额度
- 经理最高可以批准 100000 元的借款额度
当有转账的请求过来时,先由等级低的员工处理,若无法处理,则将请求移交给上级处理。
根据上面的场景,运用责任链模式,用 Java 代码表示为:
首先先定义借款请求:
class BorrowRequest {
private int requestMoney;
public BorrowRequest(int money) {
System.out.println("有新请求,需要借款 " + money + " 元");
requestMoney = money;
}
public int getMoney() {
return requestMoney;
}
}
然后再定义一个抽象职员类,用于实现责任链:
abstract class AbstractClerk {
private AbstractClerk superior = null;
protected String type;
public void setSuperior(AbstractClerk superior) {
this.superior = superior;
}
public void approveRequest(BorrowRequest request) {
if(getLimit() >= request.getMoney()) {
System.out.println(getType() + "同意借款请求");
}else {
if(this.superior != null) {
this.superior.approveRequest(request);
}else {
System.out.println("没有人能够同意借款请求");
}
}
}
public abstract int getLimit();
public String getType() {
return type;
}
}
实现完抽象职员类之后,只需要创建具体的员工类,并设置其额度就可以了。
class Clerk extends AbstractClerk{
public Clerk() {
super.type = "职员";
}
public int getLimit() {
return 5000;
}
}
class Leader extends AbstractClerk{
public Leader() {
super.type = "组长";
}
public int getLimit() {
return 20000;
}
}
class Manager extends AbstractClerk{
public Manager() {
super.type = "经理";
}
public int getLimit() {
return 100000;
}
}
最后在客户端调用:
public class Client {
public static void main(String[] args) {
AbstractClerk clerk = new Clerk();
AbstractClerk leader = new Leader();
AbstractClerk manager = new Manager();
clerk.setSuperior(leader);
leader.setSuperior(manager);
//有人借款 10000 元
clerk.approveRequest(new BorrowRequest(10000));
//有人借款 111000 元
clerk.approveRequest(new BorrowRequest(111000));
}
}
运行结果:
这时候如果添加了新的职位老版,他的额度为 1000000 元,那么我们只需创建一个新的具体职员类继承抽象职工类即可。
class Boss extends AbstractClerk{
public Boss() {
super.type = "老版";
}
public int getLimit() {
return 1000000;
}
}
public class Client {
public static void main(String[] args) {
AbstractClerk clerk = new Clerk();
AbstractClerk leader = new Leader();
AbstractClerk manager = new Manager();
AbstractClerk boss = new Boss();
clerk.setSuperior(leader);
leader.setSuperior(manager);
manager.setSuperior(boss);
//有人借款 10000 元
clerk.approveRequest(new BorrowRequest(10000));
//有人借款 111000 元
clerk.approveRequest(new BorrowRequest(111000));
}
}
运行结果:
六、责任链模式优缺点
优点:
- 降低耦合度
- 可简化对象的相互连接
- 增强给对象指派职责的灵活性
- 增加新的请求处理类很方便
缺点:
- 不能保证请求一定被接收
- 系统性能将受到一定影响,而且在进行代码调试时不太方便;可能会造成循环调用
七、适用环境
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求