Java設計模式之從[打飛機遊戲中的控制器]分析命令(Command)模式
首先請允許我囉嗦幾句。為什麼我們在軟體設計過程中強調設計模式?為軟體增加設計模式確實會增加一定的程式碼複雜程度,但是它的好處是無窮的。它可以使得軟體更加易於擴充套件而無需改變原始碼。“解耦”是設計模式中的一個關鍵詞。例如,對於某個物件obj,在呼叫obj.method()時,我們希望obj是一個基類或者是一個介面,而不是一個具體的實現類,用這種方式來實現解耦。
下面進入正題。
假設我正在做一個打飛機的遊戲,我現在正在為飛機編寫控制器。飛機的控制器主要可以操作飛機用導彈攻擊、炸彈攻擊,以及向4個方向移動。我將先給出一段“命令模式”的Java程式碼,然後再來解釋什麼叫做命令模式。
Java程式碼如下:
interface FighterCommand{ void execute(); } //接收者 class Fighter{ public void missile(){ System.out.println("用導彈進行攻擊!"); } public void bomb(){ System.out.println("用炸彈進行攻擊!"); } public void move(int direction){ switch (direction){ case 1: System.out.println("向上移動!"); break; case 2: System.out.println("向下移動!"); break; case 3: System.out.println("向左移動!"); break; case 4: System.out.println("向右移動!"); break; default: System.out.println("不移動!"); break; } } } class MissleCommand implements FighterCommand { Fighter fighter; public MissleCommand(Fighter fighter){ this.fighter = fighter; } public void execute(){ fighter.missile(); } } class BombCommand implements FighterCommand { Fighter fighter; public BombCommand(Fighter fighter){ this.fighter = fighter; } public void execute(){ fighter.bomb(); } } class MoveCommand implements FighterCommand { Fighter fighter; int direction; public MoveCommand(Fighter fighter, int direction){ this.fighter = fighter; this.direction = direction; } public void execute(){ fighter.move(direction); } } //請求者 class Controller { private FighterCommand cmdMissile; private FighterCommand cmdBomb; private FighterCommand cmdMoveLeft; private FighterCommand cmdMoveRight; public Controller (FighterCommand missile, FighterCommand bomb, FighterCommand left, FighterCommand right){ cmdMissile = missile; cmdBomb = bomb; cmdMoveLeft = left; cmdMoveRight = right; } public void missile(){ cmdMissile.execute(); } public void bomb(){ cmdBomb.execute(); } public void moveLeft(){ cmdMoveLeft.execute(); } public void moveRight(){ cmdMoveRight.execute(); } } class Command { public static void main(String[] args) { Fighter fighter1 = new Fighter(); FighterCommand cmdMissile = new MissleCommand(fighter1); FighterCommand cmdBomb = new BombCommand(fighter1); FighterCommand cmdMoveLeft = new MoveCommand(fighter1, 3); FighterCommand cmdMoveRight = new MoveCommand(fighter1, 4); Controller player1 = new Controller (cmdMissile, cmdBomb, cmdMoveLeft, cmdMoveRight); player1.bomb(); player1.missile(); player1.moveLeft(); player1.moveRight(); } }
程式執行結果如下:
用炸彈進行攻擊!
用導彈進行攻擊!
向左移動!
向右移動
請仔細體會上面程式碼的用意。命令模式主要由命令的接收者(Fighter)、命令的行為(FighterCommand介面)以及命令的請求者(Controller)組成。它的意圖是將請求封裝為一個物件(FighterCommand的派生類),從而使得你可以用不同的請求對客戶進行引數化;對請求排隊或者記錄請求日誌、支援撤銷等操作。如上面的程式碼,我們把飛機的行為(左、右移動;發射導彈;投放炸彈)分別解除安裝了3個不同的類中,而這3個類中都包含對戰鬥機Fighter的引用,戰鬥機作為動作的接收類,可以實現這些行為。我們把這些動作都聚合在了Controller類中,由Controller類發出請求資訊,請求經過了FighterCommand,最終由Fighter類接收,並且可以知道應該做出什麼樣的行為。
接下來疑惑就出現了:為什麼要煞費苦心地建立一個Controller類,為什麼不直接呼叫fighter1.bomb(); fighter1.missile()等方法呢?原因是兩個字——解耦。如本文一開始所說,當呼叫obj.method()時,我們希望obj是一個基類物件或者是一個介面物件,因為這樣它可以適用於一類方法,而不是某一個特定型別的方法。在本例中使用命令模式的好處就在於,我們將Fighter的行為與其本身的物件解耦。假設某一天,我們希望Fighter的bomb()方法發生一些變化,如在放炸彈之前先呼叫missile方法,若不用命令模式,我們需要在所有呼叫Fighter.bomb()方法之前呼叫一次Fighter.missile(),這是何等的麻煩!如果用了命令模式,我們可以建立一個新的FighterCommand,將execute方法寫為先呼叫missile()再呼叫bomb,最後修改Controller類傳入的構造引數即可。設計模式的用途在此體現:讓軟體更加易於擴充套件。