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來移動。
洞穴任務: 請消滅洞穴中的所有怪獸。
職責鏈的有點是降低了傳送請求者和接受請求者的耦合度,增強了指派職責的靈活性,它只需要指定一個候選者即可實現職責的指派。然而,它的缺點是,無法保證指派的職責一定能被接收,由於含有遞迴思想,這個模式會有一定的效能損耗,如果不注意可能會陷入迴圈呼叫之中。