Java设计模式之从[重任召唤等游戏的任务提示]分析职责链(Chain Of Responsibility)模式

Java设计模式之从[使命召唤等游戏的任务提示]分析职责链(Chain Of Responsibility)模式

  我们在使命召唤、暗黑破坏神等游戏时,总会接到各种各样的游戏任务,如到某某地方解救某人,或者消灭某某地方的敌人等。当玩家进入到某一个地图(以下称之为游戏场景)时,我们就可以查看它的任务提示。在这个机制下,我们认为,所有的游戏场景都继承于一个类(如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设计模式之从[重任召唤等游戏的任务提示]分析职责链(Chain Of Responsibility)模式

  以上就是我刚刚谈到的职责链模式。它的意图是使得对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。用直白一点的话说就是,如果我可以处理这个请求则处理之,如果不能处理,则让我的候选者来处理。

  以下是职责链模式的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来移动。
洞穴任务: 请消灭洞穴中的所有怪兽。

  职责链的有点是降低了发送请求者和接受请求者的耦合度,增强了指派职责的灵活性,它只需要指定一个候选者即可实现职责的指派。然而,它的缺点是,无法保证指派的职责一定能被接收,由于含有递归思想,这个模式会有一定的性能损耗,如果不注意可能会陷入循环调用之中。