情景
俗话说天下大势分久必合,合久必分,中原的一个大帝国维持了数百年之后,终于分崩离析,各路诸侯起兵,群雄逐鹿。
小帅自立为王,率领十万大军,挥军北上,逐鹿中原,梦想成就一番霸业。
小帅大军势如破竹接连攻下好几座城池,想要一鼓作气,拿下固若金汤的名城,盘岩城。
不料,大军久攻不下,损失惨重,眼看围城三月有余,还是毫无进展,这边又粮草告急,小帅心急如焚。
小帅问军师有何对策,军师说到:“我知道此次出征必有恶战,几个月前我已在准备秘密武器,这几日就到,到时必破此城,主公莫慌。”
果然,几日之后,军师请小帅来到阵前:“秘密武器已经抵达军中,请主公过目。”
此乃工部研制的投石机,能一次投掷几百斤重的石头,可破对方城墙。
此乃工部研制的重型冲车,士兵躲在里面,能躲避弓箭落石,可破对方城门。
最后这位是专门从西域花重金请来的魔法师,法力深厚,到时便知。
万事已备,只等主公一声令下,便可拿下此城。
小帅问道:“军师可知这些武器如何操作?”
军师答道:“主公不必知晓细节,只需下令攻击命令即可,自有操作熟练的将士操作。”
小帅大喜,赶紧召集诸将,制定作战计划。
代码实现
/**
* 命令接口
* @author zhanyd
* @date 2020-12-26
*/
public interface Command {
public void execute();
}
/**
* 具体的进攻命令类
* @author zhanyd
* @date 2020-12-26
*/
public class FireCommand {
Command command;
public void setCommand(Command command) {
this.command = command;
}
public void fire() {
command.execute();
}
}
/**
* 投石车类
*/
public class Catapult {
/**
* 装载石头
*/
public void loadStone() {
System.out.println("投石车装载石头。。。");
}
/**
* 瞄准目标
*/
public void aim() {
System.out.println("投石车瞄准目标。。。");
}
/**
* 发射
*/
public void fire() {
System.out.println("投石车发射!");
}
}
/**
* 投石车攻击类
* @author zhanyd
* @date 2020-12-26
*/
public class CatapultFireCommand implements Command{
private Catapult catapult;
public CatapultFireCommand(Catapult catapult) {
this.catapult = catapult;
}
/**
* 投石车攻击
*/
@Override
public void execute() {
catapult.loadStone();
catapult.aim();
catapult.fire();
}
}
/**
* 冲车类
*/
public class RushCar {
/**
* 移动
*/
public void move() {
System.out.println("冲车向目标移动。。。");
}
/**
* 撞击
*/
public void hit() {
System.out.println("冲车撞击!");
}
}
/**
* 冲车攻击类
* @author zhanyd
* @date 2020-12-26
*/
public class RushCarFireCommand implements Command {
private RushCar rushCar;
public RushCarFireCommand(RushCar rushCar) {
this.rushCar = rushCar;
}
/**
* 冲车攻击
*/
@Override
public void execute() {
rushCar.move();
rushCar.hit();
}
}
/**
* 魔法师类
*/
public class Magician {
/**
* 吟唱
*/
public void chant() {
System.out.println("魔法师吟唱咒语。。。");
}
/**
* 召唤陨石
*/
public void summonMeteor() {
System.out.println("魔法师召唤陨石!");
}
}
/**
* 魔法师攻击类
* @author zhanyd
* @date 2020-12-26
*/
public class MagicianFireCommand implements Command{
private Magician magician;
public MagicianFireCommand(Magician magician) {
this.magician = magician;
}
/**
* 魔法师攻击
*/
@Override
public void execute() {
magician.chant();
magician.summonMeteor();
}
}
/**
* 攻击测试类
* @author zhanyd
* @date 2020-12-26
*/
public class FireTest {
public static void main(String[] args) {
FireCommand fireCommand = new FireCommand();
// 投石车攻击
Catapult catapult = new Catapult();
CatapultFireCommand catapultFireCommand = new CatapultFireCommand(catapult);
fireCommand.setCommand(catapultFireCommand);
fireCommand.fire();
// 冲车攻击
RushCar rushCar = new RushCar();
RushCarFireCommand rushCarFireCommand = new RushCarFireCommand(rushCar);
fireCommand.setCommand(rushCarFireCommand);
fireCommand.fire();
// 魔法师攻击
Magician magician = new Magician();
MagicianFireCommand magicianFireCommand = new MagicianFireCommand(magician);
fireCommand.setCommand(magicianFireCommand);
fireCommand.fire();
}
}
投石车装载石头。。。
投石车瞄准目标。。。
投石车发射!
冲车向目标移动。。。
冲车撞击!
魔法师吟唱咒语。。。
魔法师召唤陨石!
命令模式定义
命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
命令模式的类图和上面代码中类的对应关系如下:
Invoker:就是对应FireCommand类。
Command:就是对应Command类。
ConcreteCommand:就是对应CatapultFireCommand,RushCarFireCommand,MagicianFireCommand类。
Receiver:就是对应Catapult,RushCar,Magician类。
Client:就是对应FireTest类。
批处理
随着一声声巨响,对方的城墙上硝烟四起,小帅惊叹不已,心想只要我一声令下,不管什么武器,按照各自的方式攻击就行了,都不用我操心,真是爽啊。
“报~~主公,敌方城墙出现了裂缝,但是还没打开缺口!”,前方探子来报。
小帅嗖得起身,立马来到军师面前下令:“令魔法师,投石车和冲车同时进攻!”
“得令!”军师立马把主公的命令传达了下去。
/**
* 批量进攻命令类
* @author zhanyd
* @date 2020-12-26
*/
public class BatchFireCommand {
List<Command> commandList = new ArrayList<Command>();
public void addCommand(Command command) {
this.commandList.add(command);
}
public void fire() {
commandList.stream().forEach(f -> f.execute());
}
}
/**
* 批量攻击测试类
* @author zhanyd
* @date 2020-12-26
*/
public class BathFireTest {
public static void main(String[] args) {
BatchFireCommand batchFireCommand = new BatchFireCommand();
// 魔法师攻击
Magician magician = new Magician();
MagicianFireCommand magicianFireCommand = new MagicianFireCommand(magician);
batchFireCommand.addCommand(magicianFireCommand);
// 投石车攻击
Catapult catapult = new Catapult();
CatapultFireCommand catapultFireCommand = new CatapultFireCommand(catapult);
batchFireCommand.addCommand(catapultFireCommand);
// 冲车攻击
RushCar rushCar = new RushCar();
RushCarFireCommand rushCarFireCommand = new RushCarFireCommand(rushCar);
batchFireCommand.addCommand(rushCarFireCommand);
// 一起上
batchFireCommand.fire();
}
}
魔法师吟唱咒语。。。
魔法师召唤陨石!
投石车装载石头。。。
投石车瞄准目标。。。
投石车发射!
冲车向目标移动。。。
冲车撞击!
敌方的一段城墙在这一轮攻势下,轰然倒塌,漏出一个大洞,军中欢呼声一片,小帅大手一挥,各路兵马蜂拥而上。。。
总结
命令模式是对操作的解耦,操作的发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道接收者是谁,以及接受者是如何完成请求的。这就是命令模式的精髓所在。
就像小帅只管下达“进攻”的命令,不用管到底是投石机,还是魔法师在进攻,如何进攻的,那是手下操心的事情,主公不必关心。
你是不是觉得命令模式和策略模式(策略模式玩转步兵,骑士和弓箭手)很像?
其实他们之间还是有些差别的,在策略模式中,不同的策略具有相同的目的、不同的实现、互相之间可以替换。比如,FightWithSword 、FightWithSpear 都是攻击方式,一个是用剑攻击,另一个是用长枪攻击,它们相当于不同的算法,是可以相互替换的。
而在命令模式中,不同的命令具有不同的目的,对应不同的处理逻辑,它们之间可能千差万别,并且互相之间不可替换。
二者的关注点不同:
策略模式提供多种行为由调用者自己选用,算法的自由选用是其关注点。
命令模式关注解耦,将请求的内容封装成命令由接受者执行。
二者使用场景不同:
策略模式适用于有多种行为可以相互替换的场景。
命令模式适用于解耦两个紧耦合关系的对象或多命令对象撤销的场景。