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类传入的构造参数即可。设计模式的用途在此体现:让软件更加易于扩展。