Day 19: Behavioral patterns - Command

目的

将行为拆成请求与执行两个区块,请求的部分独自封装成物件,执行的部分则交由专门负责执行的物件。
负责执行的物件除了储存等待执行的请求,还可以在执行前取消特定请求。

说明

想像一个情况,使用者可以很简单地说出要执行的行为,负责执行的程序可以迅速反应,转换到设计上,单个要执行的行为,经常包装成物件,物件内有执行的细节。

现在情况改变,有许多的行为可以执行,那要建立相同数量的物件,数量越多,越不容易管理,因为分散各地。
此外,如果需要一个历史清单追踪要执行的行为,以及取消特定行为呢?

单纯的「传球、接球」模式无法做到。

为了实践这两个新需求,可以将执行的细节封装在特定物件内,原本的包装行为的物件简化成呼叫特定物件内的对应方法。

实践的作法是:

  1. 建立特定物件(Receiver),负责各个行为的执行细节。
  2. 建立行为的虚拟层亲代,开规格要求所有行为都要有「执行」方法以及「与特定物件的连结」。
  3. 建立行为子代,在「执行」呼叫特定物件上的对应方法
  4. 建立使用者对外窗口(Invoker),负责接受、储存、取消使用者要求的行为,并在确定後执行所有行为。

以下范例以「模拟世纪帝国二的基本操作」为核心制作。

UML 图

Command Pattern UML Diagram

使用 Java 实作

单位物件:VillagerPaladinHouseCastle

public class Villager {
    private int healthPoints;
    private Headquarters headquarters;

    public Villager(Headquarters headquarters) {
        healthPoints = 25;
        this.headquarters = headquarters;
    }

    public void gotDamage(int damagePoints) {
        healthPoints -= damagePoints;

        if (healthPoints < 0) {
            headquarters.removeVillager();
        }
    }
}

public class Paladin {
    private int healthPoints;
    private Headquarters headquarters;

    public Paladin(Headquarters headquarters) {
        healthPoints = 160;
        this.headquarters = headquarters;
    }

    public void gotDamage(int damagePoints) {
        healthPoints -= damagePoints;

        if (healthPoints < 0) {
            headquarters.removePaladin();
        }
    }
}

public class House {
    private int healthPoints;
    private Headquarters headquarters;

    public House(Headquarters headquarters) {
        healthPoints = 550;
        this.headquarters = headquarters;
    }

    public void gotDamage(int damagePoints) {
        healthPoints -= damagePoints;

        if (healthPoints < 0) {
            headquarters.removeHouse();
        }
    }
}

public class Castle {
    private int healthPoints;
    private Headquarters headquarters;

    public Castle(Headquarters headquarters) {
        healthPoints = 4800;
        this.headquarters = headquarters;
    }

    public void gotDamage(int damagePoints) {
        healthPoints -= damagePoints;

        if (healthPoints < 0) {
            headquarters.removeCastle();
        }
    }
}

特定物件(Receiver)

public class Headquarters {
    private int woodCounts;
    private int foodCounts;
    private int goldCounts;
    private int stoneCounts;
    private ArrayList<Villager> villagerList = new ArrayList<>();
    private ArrayList<Paladin> paladinList = new ArrayList<>();
    private ArrayList<House> houseList = new ArrayList<>();
    private ArrayList<Castle> castleList = new ArrayList<>();

    public Headquarters() {
        woodCounts = 100;
        foodCounts = 100;
        goldCounts = 50;
        stoneCounts = 50;
    }

    public void createVillager() {
        if (foodCounts < 50) {
            System.out.println("食物不足,无法生产村民\n");
        } else {
            System.out.println("食物充足,已生产村民\n");
            villagerList.add(new Villager(this));
            foodCounts -= 50;
        }
    }

    public void removeVillager() {
        if (villagerList.isEmpty()) {
            System.out.println("没有村民可以删除");
        } else {
            villagerList.remove(0);
            System.out.println("村民已删除");
        }
    }

    public void createPaladin() {
        if (foodCounts < 60) {
            System.out.println("食物不足,无法生产游侠\n");
        } else if (goldCounts < 75) {
            System.out.println("黄金不足,无法生产游侠\n");
        } else {
            System.out.println("食物充足,已生产游侠\n");
            paladinList.add(new Paladin(this));
            foodCounts -= 60;
            goldCounts -= 75;
        }
    }

    public void removePaladin() {
        if (paladinList.isEmpty()) {
            System.out.println("没有游侠可以删除");
        } else {
            paladinList.remove(0);
            System.out.println("游侠已删除");
        }
    }

    public void createHouse() {
        if (woodCounts < 25) {
            System.out.println("木头不足,无法建造居住房舍\n");
        } else {
            System.out.println("木头充足,已建造居住房舍\n");
            houseList.add(new House(this));
            woodCounts -= 25;
        }
    }

    public void removeHouse() {
        if (houseList.isEmpty()) {
            System.out.println("没有居住房舍可以删除");
        } else {
            houseList.remove(0);
            System.out.println("居住房舍已删除");
        }
    }

    public void createCastle() {
        if (stoneCounts < 650) {
            System.out.println("石头不足,无法建造城堡\n");
        } else {
            System.out.println("石头充足,已建造城堡\n");
            castleList.add(new Castle(this));
            stoneCounts -= 600;
        }
    }

    public void removeCastle() {
        if (castleList.isEmpty()) {
            System.out.println("没有城堡可以删除");
        } else {
            castleList.remove(0);
            System.out.println("城堡已删除");
        }
    }

    public void gatherWood() {
        if (villagerList.isEmpty()) {
            System.out.println("没有村民可用\n");
        } else {
            System.out.println("村民数量足够,开始砍树\n");
            woodCounts += 100;
        }
    }

    public void gatherFood() {
        if (villagerList.isEmpty()) {
            System.out.println("没有村民可用\n");
        } else {
            System.out.println("村民数量足够,开始采集食物\n");
            foodCounts += 100;
        }
    }

    public void gatherGold() {
        if (villagerList.isEmpty()) {
            System.out.println("没有村民可用\n");
        } else {
            System.out.println("村民数量足够,开始挖金矿\n");
            goldCounts += 100;
        }
    }

    public void gatherStone() {
        if (villagerList.isEmpty()) {
            System.out.println("没有村民可用\n");
        } else {
            System.out.println("村民数量足够,开始挖石矿\n");
            stoneCounts += 100;
        }
    }

    public void showResources() {
        System.out.println("---当前资源---");
        System.out.println("木头: " + woodCounts);
        System.out.println("食物: " + foodCounts);
        System.out.println("黄金: " + goldCounts);
        System.out.println("石头: " + stoneCounts);
        System.out.println("---当前建筑数量---");
        System.out.println("居住房舍: " + houseList.size());
        System.out.println("城堡: " + castleList.size());
        System.out.println("---当前单位数量---");
        System.out.println("村民: " + villagerList.size());
        System.out.println("游侠: " + paladinList.size());
        System.out.println("---完毕---\n");
    }
}

行为的虚拟层亲代:Command

public abstract class Command {
    protected Headquarters headquarters;

    protected Command(Headquarters headquarters) {
        this.headquarters = headquarters;
    }

    public abstract void executeCommand();

    public abstract String getName();
}

行为子代:CreateVillagerCommandRemoveVillagerCommandCreatePaladinCommandRemovePaladinCommandCreateHouseCommandRemoveHouseCommandCreateCastleCommandRemoveCastleCommandGatherFoodCommandGatherGoldCommandGatherStoneCommandGatherWoodCommandShowResourcesCommand(Command 物件)

public class CreateVillagerCommand extends Command {
    private String name = "生产村民";

    public CreateVillagerCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.createVillager();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class RemoveVillagerCommand extends Command {
    private String name = "删除村民";

    public RemoveVillagerCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.removeVillager();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class CreatePaladinCommand extends Command {
    private String name = "生产游侠";

    public CreatePaladinCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.createPaladin();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class RemovePaladinCommand extends Command {
    private String name = "删除游侠";

    public RemovePaladinCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.removePaladin();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class CreateHouseCommand extends Command {
    private String name = "建造居住房舍";

    public CreateHouseCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.createHouse();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class RemoveHouseCommand extends Command {
    private String name = "删除居住房舍";

    public RemoveHouseCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.removeHouse();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class CreateCastleCommand extends Command {
    private String name = "建造城堡";

    public CreateCastleCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.createCastle();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class RemoveCastleCommand extends Command {
    private String name = "删除城堡";

    public RemoveCastleCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.removeCastle();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class GatherFoodCommand extends Command {
    private String name = "采集食物";

    public GatherFoodCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.gatherFood();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class GatherGoldCommand extends Command {
    private String name = "挖金矿";

    public GatherGoldCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.gatherGold();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class GatherStoneCommand extends Command {
    private String name = "挖石矿";

    public GatherStoneCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.gatherStone();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class GatherWoodCommand extends Command {
    private String name = "砍树";

    public GatherWoodCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.gatherWood();
    }

    @Override
    public String getName() {
        return name;
    }
}

public class ShowResourcesCommand extends Command {
    private String name = "显示资源、建筑与单位";

    public ShowResourcesCommand(Headquarters headquarters) {
        super(headquarters);
    }

    @Override
    public void executeCommand() {
        headquarters.showResources();
    }

    @Override
    public String getName() {
        return name;
    }
}

对外窗口(Invoker):GUI

public class GUI {
    private ArrayList<Command> orders;

    public GUI() {
        this.orders = new ArrayList<>();
    }

    public void receivePlayerCommand(Command command) {
        orders.add(command);
        System.out.println("接收指令: " + command.getName() + " 时间:" + new Date());
    }

    public void cancelPlayerCommand(Command command) {
        orders.remove(command);
        System.out.println("取消指令: " + command.getName() + " 时间:" + new Date());
    }

    public void executePlayerCommands() {
        for (Command command : orders) {
            command.executeCommand();
        }
    }
}

测试,玩家尝试建造房屋、城堡,生产村民、游侠,采集木头、食物、黄金、石头,最後显示资源与单位数量:RTSCommandSample

public class RTSCommandSample {
    public static void main(String[] args) {
        Headquarters headquarters = new Headquarters();
        Command createVillagerCommand = new CreateVillagerCommand(headquarters);
        Command createPaladinCommand = new CreatePaladinCommand(headquarters);
        Command createHouseCommand = new CreateHouseCommand(headquarters);
        Command createCastleCommand = new CreateCastleCommand(headquarters);
        Command gatherWoodCommand = new GatherWoodCommand(headquarters);
        Command gatherFoodCommand = new GatherFoodCommand(headquarters);
        Command gatherGoldCommand = new GatherGoldCommand(headquarters);
        Command gatherStoneCommand = new GatherStoneCommand(headquarters);
        Command showResourcesCommand = new ShowResourcesCommand(headquarters);
        GUI gui = new GUI();

        gui.receivePlayerCommand(createVillagerCommand);

        gui.receivePlayerCommand(createPaladinCommand);
        gui.receivePlayerCommand(gatherFoodCommand);
        gui.receivePlayerCommand(gatherFoodCommand);
        gui.receivePlayerCommand(gatherGoldCommand);
        gui.receivePlayerCommand(gatherGoldCommand);
        gui.receivePlayerCommand(createPaladinCommand);

        gui.receivePlayerCommand(createHouseCommand);
        gui.receivePlayerCommand(createHouseCommand);
        gui.receivePlayerCommand(createHouseCommand);
        gui.receivePlayerCommand(createHouseCommand);
        gui.receivePlayerCommand(createHouseCommand);
        gui.receivePlayerCommand(gatherWoodCommand);
        gui.receivePlayerCommand(createHouseCommand);
        gui.cancelPlayerCommand(createHouseCommand);
        gui.cancelPlayerCommand(createHouseCommand);

        gui.receivePlayerCommand(createCastleCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(gatherStoneCommand);
        gui.cancelPlayerCommand(gatherStoneCommand);
        gui.receivePlayerCommand(createCastleCommand);

        gui.receivePlayerCommand(showResourcesCommand);

        gui.executePlayerCommands();
    }
}

使用 JavaScript 实作

单位物件:VillagerPaladinHouseCastle

class Villager {
  /** @param {Headquarters} headquarters */
  constructor(headquarters) {
    this.healthPoints = 25;
    this.headquarters = headquarters;
  }

  /** @param {number} damagePoints */
  /** @param {number} damagePoints */
  gotDamage(damagePoints) {
    this.healthPoints -= damagePoints;

    if (this.healthPoints < 0) {
      this.headquarters.removeVillager();
    }
  }
}

class Paladin {
  /** @param {Headquarters} headquarters */
  constructor(headquarters) {
    this.healthPoints = 160;
    this.headquarters = headquarters;
  }

  /** @param {number} damagePoints */
  gotDamage(damagePoints) {
    this.healthPoints -= damagePoints;

    if (this.healthPoints < 0) {
      this.headquarters.removePaladin();
    }
  }
}

class House {
  /** @param {Headquarters} headquarters */
  constructor(headquarters) {
    this.healthPoints = 550;
    this.headquarters = headquarters;
  }

  /** @param {number} damagePoints */
  gotDamage(damagePoints) {
    this.healthPoints -= damagePoints;

    if (this.healthPoints < 0) {
      this.headquarters.removeHouse();
    }
  }
}

class Castle {
  /** @param {Headquarters} headquarters */
  constructor(headquarters) {
    this.healthPoints = 4800;
    this.headquarters = headquarters;
  }

  /** @param {number} damagePoints */
  gotDamage(damagePoints) {
    this.healthPoints -= damagePoints;

    if (this.healthPoints < 0) {
      this.headquarters.removeCastle();
    }
  }
}

特定物件(Receiver)

class Headquarters {
  constructor() {
    this.woodCounts = 100;
    this.foodCounts = 100;
    this.goldCounts = 50;
    this.stoneCounts = 50;
    /** @type {Villager[]} */
    this.villagerList = [];
    /** @type {Paladin[]} */
    this.paladinList = [];
    /** @type {House[]} */
    this.houseList = [];
    /** @type {Castle[]} */
    this.castleList = [];
  }

  createVillager() {
    if (this.foodCounts < 50) {
      console.log("食物不足,无法生产村民\n");
    } else {
      console.log("食物充足,已生产村民\n");
      this.villagerList.push(new Villager(this));
      this.foodCounts -= 50;
    }
  }

  removeVillager() {
    if (this.villagerList.length === 0) {
      console.log("没有村民可以删除");
    } else {
      this.villagerList.shift();
      console.log("村民已删除");
    }
  }

  createPaladin() {
    if (this.foodCounts < 60) {
      console.log("食物不足,无法生产游侠\n");
    } else if (this.goldCounts < 75) {
      console.log("黄金不足,无法生产游侠\n");
    } else {
      console.log("食物充足,已生产游侠\n");
      this.paladinList.push(new Paladin(this));
      this.foodCounts -= 60;
      this.goldCounts -= 75;
    }
  }

  removePaladin() {
    if (this.paladinList.length === 0) {
      console.log("没有游侠可以删除");
    } else {
      this.paladinList.shift();
      console.log("游侠已删除");
    }
  }

  createHouse() {
    if (this.woodCounts < 25) {
      console.log("木头不足,无法建造居住房舍\n");
    } else {
      console.log("木头充足,已建造居住房舍\n");
      this.houseList.push(new House(this));
      this.woodCounts -= 25;
    }
  }

  removeHouse() {
    if (houseList.length === 0) {
      console.log("没有居住房舍可以删除");
    } else {
      this.houseList.shift();
      console.log("居住房舍已删除");
    }
  }

  createCastle() {
    if (this.stoneCounts < 650) {
      console.log("石头不足,无法建造城堡\n");
    } else {
      console.log("石头充足,已建造城堡\n");
      this.castleList.push(new Castle(this));
      this.stoneCounts -= 600;
    }
  }

  removeCastle() {
    if (this.castleList.length === 0) {
      console.log("没有城堡可以删除");
    } else {
      this.castleList.shift();
      console.log("城堡已删除");
    }
  }

  gatherWood() {
    if (this.villagerList.length === 0) {
      console.log("没有村民可用\n");
    } else {
      console.log("村民数量足够,开始砍树\n");
      this.woodCounts += 100;
    }
  }

  gatherFood() {
    if (this.villagerList.length === 0) {
      console.log("没有村民可用\n");
    } else {
      console.log("村民数量足够,开始采集食物\n");
      this.foodCounts += 100;
    }
  }

  gatherGold() {
    if (this.villagerList.length === 0) {
      console.log("没有村民可用\n");
    } else {
      console.log("村民数量足够,开始挖金矿\n");
      this.goldCounts += 100;
    }
  }

  gatherStone() {
    if (this.villagerList.length === 0) {
      console.log("没有村民可用\n");
    } else {
      console.log("村民数量足够,开始挖石矿\n");
      this.stoneCounts += 100;
    }
  }

  showResources() {
    console.log("---当前资源---");
    console.log("木头: " + this.woodCounts);
    console.log("食物: " + this.foodCounts);
    console.log("黄金: " + this.goldCounts);
    console.log("石头: " + this.stoneCounts);
    console.log("---当前建筑数量---");
    console.log("居住房舍: " + this.houseList.length);
    console.log("城堡: " + this.castleList.length);
    console.log("---当前单位数量---");
    console.log("村民: " + this.villagerList.length);
    console.log("游侠: " + this.paladinList.length);
    console.log("---完毕---\n");
  }
}

行为的虚拟层亲代:Command

/** @abstract */
class Command {
  /** @param {Headquarters} headquarters  */
  constructor(headquarters) {
    this.headquarters = headquarters;
  }

  /** @abstract */
  executeCommand() { return; }

  /** @abstract */
  getName() { return; }
}

行为子代:CreateVillagerCommandRemoveVillagerCommandCreatePaladinCommandRemovePaladinCommandCreateHouseCommandRemoveHouseCommandCreateCastleCommandRemoveCastleCommandGatherFoodCommandGatherGoldCommandGatherStoneCommandGatherWoodCommandShowResourcesCommand(Command 物件)

class CreateVillagerCommand extends Command {
  /** @param {Headquarters} headquarters */
  constructor(headquarters) {
    super(headquarters);
    this.name = "生产村民";
  }

  /** @override */
  executeCommand() {
    this.headquarters.createVillager();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class RemoveVillagerCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "删除村民";
  }

  /** @override */
  executeCommand() {
    this.headquarters.removeVillager();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class CreatePaladinCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "生产游侠";
  }

  /** @override */
  executeCommand() {
    this.headquarters.createPaladin();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class RemovePaladinCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "删除游侠";
  }

  /** @override */
  executeCommand() {
    this.headquarters.removePaladin();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class CreateHouseCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "建造居住房舍";
  }

  /** @override */
  executeCommand() {
    this.headquarters.createHouse();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class RemoveHouseCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "删除居住房舍";
  }

  /** @override */
  executeCommand() {
    this.headquarters.removeHouse();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class CreateCastleCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "建造城堡";
  }

  /** @override */
  executeCommand() {
    this.headquarters.createCastle();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class RemoveCastleCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "删除城堡";
  }

  /** @override */
  executeCommand() {
    this.headquarters.removeCastle();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class GatherFoodCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "采集食物";
  }

  /** @override */
  executeCommand() {
    this.headquarters.gatherFood();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class GatherGoldCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "挖金矿";
  }

  /** @override */
  executeCommand() {
    this.headquarters.gatherGold();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class GatherStoneCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "挖石矿";
  }

  /** @override */
  executeCommand() {
    this.headquarters.gatherStone();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class GatherWoodCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "砍树";
  }

  /** @override */
  executeCommand() {
    this.headquarters.gatherWood();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

class ShowResourcesCommand extends Command {
  constructor(headquarters) {
    super(headquarters);
    this.name = "显示资源、建筑与单位";
  }

  /** @override */
  executeCommand() {
    this.headquarters.showResources();
  }

  /** @override */
  getName() {
    return this.name;
  }
}

对外窗口(Invoker):GUI

class GUI {
  constructor() {
    /** @type {Command[]} */
    this.orders = [];
  }

  receivePlayerCommand(command) {
    this.orders.push(command);
    console.log("接收指令: " + command.getName() + " 时间:" + new Date().toISOString());
  }

  cancelPlayerCommand(command) {
    this.orders.filter((item) => item !== command);
    let index = -1;

    for (let i = 0; i < this.orders.length; i++) {
      const item = this.orders[i];

      if (item.getName() === command.getName()) {
        index = i;
        break;
      }
    }

    if (index >= 0) {
      this.orders.splice(index, 1);
    }

    console.log("JSON.stringify(this.orders) " + JSON.stringify(this.orders))
    console.log("取消指令: " + command.getName() + " 时间:" + new Date().toISOString());
  }

  executePlayerCommands() {
    for (const command of this.orders) {
      command.executeCommand();
    }
  }
}

测试,玩家尝试建造房屋、城堡,生产村民、游侠,采集木头、食物、黄金、石头,最後显示资源与单位数量:rtsCommandSample

const rtsCommandSample = () => {
  const headquarters = new Headquarters();
  const createVillagerCommand = new CreateVillagerCommand(headquarters);
  const createPaladinCommand = new CreatePaladinCommand(headquarters);
  const createHouseCommand = new CreateHouseCommand(headquarters);
  const createCastleCommand = new CreateCastleCommand(headquarters);
  const gatherWoodCommand = new GatherWoodCommand(headquarters);
  const gatherFoodCommand = new GatherFoodCommand(headquarters);
  const gatherGoldCommand = new GatherGoldCommand(headquarters);
  const gatherStoneCommand = new GatherStoneCommand(headquarters);
  const showResourcesCommand = new ShowResourcesCommand(headquarters);
  const gui = new GUI();

  gui.receivePlayerCommand(createVillagerCommand);

  gui.receivePlayerCommand(createPaladinCommand);
  gui.receivePlayerCommand(gatherFoodCommand);
  gui.receivePlayerCommand(gatherFoodCommand);
  gui.receivePlayerCommand(gatherGoldCommand);
  gui.receivePlayerCommand(gatherGoldCommand);
  gui.receivePlayerCommand(createPaladinCommand);

  gui.receivePlayerCommand(createHouseCommand);
  gui.receivePlayerCommand(createHouseCommand);
  gui.receivePlayerCommand(createHouseCommand);
  gui.receivePlayerCommand(createHouseCommand);
  gui.receivePlayerCommand(createHouseCommand);
  gui.receivePlayerCommand(gatherWoodCommand);
  gui.receivePlayerCommand(createHouseCommand);
  gui.cancelPlayerCommand(createHouseCommand);
  gui.cancelPlayerCommand(createHouseCommand);

  gui.receivePlayerCommand(createCastleCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(gatherStoneCommand);
  gui.cancelPlayerCommand(gatherStoneCommand);
  gui.receivePlayerCommand(createCastleCommand);

  gui.receivePlayerCommand(showResourcesCommand);

  gui.executePlayerCommands();
}

rtsCommandSample();

总结

Command 模式是十分常见,将「行为请求者」与「行为执行者」切开来,避免两者过度耦合,增加开发上的弹性。

但是缺点十分明显,程序码为了实践分离目的,需要增加许多的程序码,同时增加程序的复杂度。部份文章指出为了实践设计模式会增加繁杂的设计、复杂度等等,今天总算体验到了。

明天将介绍 Behavioural patterns 的第三个模式:Interpreter 模式。


<<:  Day 19 Knative Serving DNS 测试

>>:  不只懂 Vue 语法:试解释 hash 与 history 模式的分别? 为何 history 模式会回传 404?

Django - Websocket 网站实时线上人数

这次要藉由websocket做出网站实时的线上人数,关於django的websocket设定就不赘述...

Day27- Go with Redis

前言 前两篇我们介绍了如何在 Go 对 MySQL 和 Scylla 做操作,而这两个皆为较具规模的...

使用回归分析与其意义 | ML#Day15

选模型并非最重要 为什麽我们要以回归分析的方式来建立模型,其他方式可不可以?其实没有说不行。 然而回...

30天零负担轻松学会制作APP介面及设计【DAY 12】

大家好,我是YIYI,今天我要来制作HOMEPAGE中MONTH与TODAY的切换以及右下角的加号点...

Day22 - 【概念篇】Keycloak使用基本概念 - 前导

本系列文之後也会置於个人网站 在这之前,都是先请大家照着做,没有好好说明关於Keycloak的使用...