首先请允许我啰嗦几句。为什么我们在软件设计过程中强调设计模式?为软件增加设计模式确实会增加一定的代码复杂程度,但是它的好处是无穷的。它可以使得软件更加易于扩展而无需改变源代码。“解耦”是设计模式中的一个关键词。例如,对于某个对象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类传入的构造参数即可。设计模式的用途在此体现:让软件更加易于扩展。