我们在使命召唤、暗黑破坏神等游戏时,总会接到各种各样的游戏任务,如到某某地方解救某人,或者消灭某某地方的敌人等。当玩家进入到某一个地图(以下称之为游戏场景)时,我们就可以查看它的任务提示。在这个机制下,我们认为,所有的游戏场景都继承于一个类(如HelpHandler),这个类包含一个显示任务提示的方法(如showHelp)。问题在于,任务的提示是有“上下文联系的”,任务的提示是和你的“场景是如何移动的”有关。例如,场景A中没有任务提示,场景B中没有任务提示,场景C、D中分别有任务提示MessageC和MessageD,如果我们从C->B->A移动,那么我们查看的任务提示就是MessageC,如果我们从D->B->A,那么任务提示就是MessageD。我们可以这样来设计:A、B、C、D这4个场景都继承于同一个类(如HelpHandler),因此它们都有显示任务提示的showHelp方法。它们内部有对另外的一个HelpHandler对象的引用,我们称之为候选者,如果自身无法处理任务提示,则调用候选者的showHelp方法。如果看到这里不大明白,就请看下图、示例和我下面的解释:
以上就是我刚刚谈到的职责链模式。它的意图是使得对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。用直白一点的话说就是,如果我可以处理这个请求则处理之,如果不能处理,则让我的候选者来处理。
以下是职责链模式的Java示例代码:
interface Helpful {
void help();
}
abstract class HelpHandler {
HelpHandler successor;
Helpful help;
public HelpHandler(){}
public void setHandler(HelpHandler parent, Helpful help){
this.successor = parent;
this.help = help;
}
public void showHelp(){
if (hasHelp()){
help.help();
} else {
if (successor != null) successor.showHelp();
}
}
private boolean hasHelp(){
return help != null;
}
}
class World extends HelpHandler {
public World(HelpHandler parent) {
Helpful help = new Helpful (){
public void help() {
System.out.println("大地图任务: 请按W、S、A、D来移动。");
}
};
setHandler(parent, help);
}
}
class House extends HelpHandler {
public House(HelpHandler parent) {
Helpful help = new Helpful (){
public void help() {
System.out.println("房间任务: 请拿起房间的武器出门。");
}
};
setHandler(parent, help);
}
}
class Cave extends HelpHandler {
public Cave(HelpHandler parent) {
Helpful help = new Helpful (){
public void help() {
System.out.println("洞穴任务: 请消灭洞穴中的所有怪兽。");
}
};
setHandler(parent, help);
}
}
class Island extends HelpHandler {
public Island(HelpHandler parent) {
setHandler(parent, null);
}
}
public class Responsibility {
public static void main(String[] args) {
World world = new World(null);
House house = new House(world);
Cave cave = new Cave(world);
Island island = new Island(world);
Island islandWithCave = new Island(cave);
world.showHelp();
house.showHelp();
cave.showHelp();
island.showHelp();
islandWithCave.showHelp();
}
}
简要分析上面的代码。上述代码是模拟在游戏的不同场景下显示任务提示。所有的场景都继承于HelpHandler类,HelpHandler类中有个HelpHandler类型的引用successor,表示处理方法的候选者。我们看看HelpHandler中的showHelp方法,它首先判断本类是否能够显示任务提示(即hasHelp是否返回true),如果可以显示则显示之,不能显示,则调用候选者的showHelp方法。通过这种方法,可以使HelpHandler类连成“链状”。
值得注意的是,我们需要在HelpHandler派生类中构造一个匿名类,这个匿名类继承于Helpful接口,这个是显示任务提示的一个接口。在不同的派生类中,我们构造不同的Helpful接口的对象来实现显示不同的任务提示,这其实是一种策略(Strategy)模式(以后会提到)。
再看看main方法,我们定义的World、House、Cave类都有自己的任务提示,而Island类没有,所以它的任务提示取决于它的候选者,因此,island的任务提示就是其候选者world的任务提示,islandWithCave的任务提示就是其候选者cave的任务提示,因此程序的结果为:
大地图任务: 请按W、S、A、D来移动。
房间任务: 请拿起房间的武器出门。
洞穴任务: 请消灭洞穴中的所有怪兽。
大地图任务: 请按W、S、A、D来移动。
洞穴任务: 请消灭洞穴中的所有怪兽。
职责链的有点是降低了发送请求者和接受请求者的耦合度,增强了指派职责的灵活性,它只需要指定一个候选者即可实现职责的指派。然而,它的缺点是,无法保证指派的职责一定能被接收,由于含有递归思想,这个模式会有一定的性能损耗,如果不注意可能会陷入循环调用之中。