1. 程式人生 > >Java設計模式之從[打飛機遊戲中的控制器]分析命令(Command)模式

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類傳入的構造引數即可。設計模式的用途在此體現:讓軟體更加易於擴充套件。