场景
击鼓传花是一种热闹而又紧张的饮酒游戏。
在酒宴上宾客依次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。
开始击鼓时,花束就开始依次传递,鼓声一落,假如花束在某人手中,则该人就得饮酒。
击鼓传花便是资任链模式的应用。
意图
在责任链模式里,很多的对象由每一个对象对其下家的引用而联接起来形成一条链。
请求在这个链上传递。
直到链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求。
这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
应用场景
1、系统已经有一个由处理者对象组成的链。这个链可能由复合模式给出,
2、当有多于一个的处理者对象会处理一个请求,而且在事先并不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的。
3、当系统想发出一个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会处理此请求。
4、当处理一个请求的处理者对象集合需要动态地指定时。
应用实例
红楼梦中,贾母、贾赦、贾政、贾宝玉和贾环是五个参力口击鼓传花游戏的传花者,他们组成一个环链。
击鼓者将花传给贾母,开始传花游戏。
花由贾母传给贾赦,由贾赦传给贾政,由贾政传给贾宝玉,又由贾宝玉传给贾环,由贾环传回给贾母,如此往复(见下图)。
当鼓声停止时,手中有花的人就得执行酒令。
在击鼓传花游戏里面,有下面的几种角色:
抽象传花者,或Handle角色、定义出参加游戏的传花人要遵守的规则,也就是一个处理请求的接口和对下家的引用;
具体传花者,或ConcreteHandle角色、每一个传花者都知道下家是谁,要么执行酒令,要么把花传下去(这个角色由贾母、贾赦、贾珍、贾琏、贾蓉、贾政、贾宝玉、贾兰等扮演。)
击鼓人,或Client角色、即行酒令的击鼓之人。《红楼梦》没有给出此人的其体姓名,只是说由‘一媳妇’扮演。
代码如下:
1.抽象传花者(Handle角色)
抽象类Player给出了两个方法的实现,一个是setsuccessor(),另一个是next()。
前者用来设置一个传花者对象的下家,后者用来将酒令传给下家。
另外,Player类给出了一个抽象方法handle(),代表执行酒令。
/** * 抽象传花者Play类的源代码。 */
public abstract class Player {
public abstract void handle(int i);
private Player successor;
public Player(){
successor = null;
}
public void setSuccessor(Player successor) {
this.successor = successor;
}
public void next(int index){
if(successor!=null){
successor.handle(index);
}else{
System.out.println("program terminated!");
}
}
}
2.具体传花者(ConcreteHandle角色)
/** * 代表贾母的JiaMu类 */
public class JiaMu extends Player {
public JiaMu(Player aSuccessor){
this.setSuccessor(aSuccessor);
}
@Override
public void handle(int i) {
if (i == 1) {
System.out.println("Jia Mu gotta drink!");
}else {
System.out.println("Jia Mu passed!");
next(i);
}
}
}
/** * 代表贾赦的JiaShe类 */
public class JiaShe extends Player {
public JiaShe(Player aSuccessor){
this.setSuccessor(aSuccessor);
}
@Override
public void handle(int i) {
if (i == 2) {
System.out.println("Jia She gotta drink!");
}else {
System.out.println("Jia She passed!");
next(i);
}
}
}
/** * 代表贾政的JiaZheng类 */
public class JiaZheng extends Player {
public JiaZheng(Player aSuccessor){
this.setSuccessor(aSuccessor);
}
@Override
public void handle(int i) {
if (i == 3) {
System.out.println("Jia Zheng gotta drink!");
}else {
System.out.println("Jia Zheng passed!");
next(i);
}
}
}
/** * 代表贾宝玉的JiaBaoYu类 */
public class JiaBaoYu extends Player {
public JiaBaoYu(Player aSuccessor){
this.setSuccessor(aSuccessor);
}
@Override
public void handle(int i) {
if (i == 4) {
System.out.println("Jia Bao Yu gotta drink!");
}else {
System.out.println("Jia Bao Yu passed!");
next(i);
}
}
}
/** * 代表贾环的JiaHuan类 */
public class JiaHuan extends Player {
public JiaHuan(Player aSuccessor){
this.setSuccessor(aSuccessor);
}
@Override
public void handle(int i) {
if (i == 5) {
System.out.println("Jia Huan gotta drink!");
}else {
System.out.println("Jia Huan passed!");
next(i);
}
}
}
3.击鼓人(Client角色)
/** * 实现的DrumBeater类在把请求传给贾母时,同时指定了由4号传花者处理酒令。 */
public class DrumBeater {
private static Player player;
public static void main(String[] args) {
player = new JiaMu( new JiaShe( new JiaZheng( new JiaBaoYu(new JiaHuan(null)))));
Thread thread=new Thread(new Runnable() {
@Override
public void run()
{
player.handle(4);
}
});
thread.start();
}
}
这里简单的模拟了一下击鼓传花系统。
责任链模式的优点
责任链模式减低了发出命令的对象和处理命令的对象之间的耦合,它允许多与一个的处理者对象根据自己的逻辑来决定哪一个处理者最终处理这个命令。
换言之,发出命令的对象只是把命令传给链结构的起始者,而不需要知道到底是链上的哪一个节点处理了这个命令。
显然,这意味着在处理命令上,允许系统有更多的灵活性。
哪一个对象最终处理一个命令可以因为由那些对象参加责任链、以及这些对象在责任链上的位置不同而有所不同。