1. 程式人生 > >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示例程式碼:

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來移動。
洞穴任務: 請消滅洞穴中的所有怪獸。

  職責鏈的有點是降低了傳送請求者和接受請求者的耦合度,增強了指派職責的靈活性,它只需要指定一個候選者即可實現職責的指派。然而,它的缺點是,無法保證指派的職責一定能被接收,由於含有遞迴思想,這個模式會有一定的效能損耗,如果不注意可能會陷入迴圈呼叫之中。