今天我们讲命令模式,首先我们先从一个故事开始。
NBA2018-2019赛季,季后赛正如火如荼的进行中,今天来到的比赛正是西部总决赛休斯顿火箭队与金州勇士队的天王山之战。
这边火箭主帅德安东尼所派出的先发五虎是:哈登,保罗,卡佩拉,塔克,恩尼斯。勇士首发:库里,克莱,杜兰特,追梦,一哥
双方比赛开始库里,和杜兰特立马进入状态,为勇士先得5分,接着火箭这边又不甘示弱,由哈登一个超远三分还以颜色。比赛进行着,一个登哥始终不如对面几个全明星来的强大。火箭主帅见状不利,连忙叫了一个暂停,戈登换下了保罗,格林换下了塔克。
保罗,恩尼斯(下场)
戈登,格林(上场)
比赛继续,戈登和格林的上场立马为火箭扭转颓势,里突外投。连忙追上比分。
双方持续焦灼着,来到第二节。德安东尼见勇士队改打三角进攻,于是叫了暂停,换了场上的所有队员
哈登,卡佩拉,格林,戈登,塔克(下场)
保罗,恩尼斯,内内,周琦,奈特(上场)
这一上场,不得了,周琦有如神助一般,罚球线起跳一个战斧把追梦格林扣翻在地。可是伤愈付出的奈特不在状态,他又撤下了奈特让塔克上场,最终在周琦带领下火箭拿下了这场天王山之战,晋级NBA总决赛。(我保证该故事不带任何个人感情色彩)
在命令模式里,有三个主要角色。调用者(Invoker),命令(Command),接收者(Receiver)。在这个故事中,主教练德安东尼就是调用者,让“保罗换下戈登”就是一个具体命令,接收者是球员。
在命令中还要支持撤销功能,便形成如下类图
这样就能将三者Invoke,Command,Receiver相互解耦,互不影响对方实现。
接下来我们实现代码
package nba;
/**
* Created by huangx on 2018/11/22.
*/
public interface Command {
void execute();
void undo();
}
package nba;
/**
* AttackCommand 进攻命令
* Created by huangx on 2018/11/23.
*/
public class AttackCommand implements Command {
private Player attackPlayer;
public AttackCommand(Player attackPlayer) {
this.attackPlayer = attackPlayer;
}
public void execute() {
attackPlayer.attact();
}
public void undo() {
attackPlayer.defend();
}
}
package nba;
/**
* 换人命令
* Created by huangx on 2018/11/22.
*/
public class SubstituteCommand implements Command {
private Player upPlayer;
private Player downPlay;
public SubstituteCommand(Player up,Player down){
this.upPlayer=up;
this.downPlay=down;
}
public void execute() {
upPlayer.up();
downPlay.down();
}
public void undo() {
downPlay.up();
upPlayer.down();
}
}
package nba;
/**
* Coach就是调用者Invoke
* Created by huangx on 2018/11/22.
*/
public class Coach {
private Command command; //如果是宏命令可将该Command设置成list
//设值注入
public void setCommand(Command command) {
this.command = command;
}
//业务方法,用于调用命令类的execute()方法
public void call() {
command.execute();
}
//撤销命令
public void undo() {
command.undo();
}
}
package nba;
/**
* Player 球员即 接收者
* Created by huangx on 2018/11/22.
*/
public class Player {
private String name;
public Player(String name) {
this.name = name;
}
public void up() {
System.out.println(name + "正兴奋的上场");
}
public void down() {
System.out.println(name + "正失望的下场");
}
public void attact() {
System.out.println(name + "正血腥的进攻");
}
public void defend() {
System.out.println(name + "正强悍的防守");
}
}
package nba;
/**
* nba西部总决赛
* Created by huangx on 2018/11/22.
*/
public class NBAGame {
public static void main(String[] args) {
//德安东尼下命令让戈登换下保罗
Player paul = new Player("gooden");
Player gooden = new Player("paul");
Command command = new SubstituteCommand(paul, gooden);
Coach antoni = new Coach();
antoni.setCommand(command);
antoni.call();
//德安东尼见势不对,撤销了刚下的命令,让保罗回来
antoni.undo();
//德安东尼让哈登趁热打铁,努力进攻
Player harder = new Player("harder");
Command harderattactCommand = new AttackCommand(harder);
antoni.setCommand(harderattactCommand);
antoni.call();
}
}
同样该模式也支持宏命令,球场上球员五上五下就可以用宏命令来实现,将coach类里面的command维护成一个List集合便可。
命令模式定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作
要具体理解的话,还是需要多看代码。