命令模式,就是把客户端(看作main方法)操作一个功能类的过程,单独抽出来作为一个独立的对象。
比如我们用游戏手柄打游戏,我们操控游戏手柄上的前进、后退、攻击等按键来发送我们的请求到游戏角色(请求接收者),游戏角色进行前进、后退、攻击的行为,先看图:
归纳一下基本结构:
- 玩家,视作main方法
- 游戏手柄,invoke类,有着所有操作的按钮(对命令类的引用)
- 命令接口(把各种命令独立出来作为单独一个实现类的统一的接口)
- 各种实际命令(实现了命令接口的命令实现类)
- 游戏角色(命令的接收者,真正执行命令的载体)
接下来用代码结合例子来详细的介绍一下命令模式:
1.首先我们先把游戏角色创建出来吧
//角色简单拥有前进、后退、攻击三个功能
public class Role
{
public void march(){
System.out.println("角色前进");
}
public void back(){
System.out.println("角色后退");
}
public void attack(){
System.out.println("角色攻击");
}
}
2.我们再创建一个命令的接口,然后把控制角色的三种命令单独作为对象实现命令接口
public interface Command{//命令接口
public void execute();
}
public class CommandMarch implements Command{//实现命令接口的前进命令实现类
private Role role;
public CommandMarch(Role role){
this.role = role;
}
@Override
public void execute() {
role.march();//控制游戏角色进行前进
}
}
public class CommandBack implements Command{//实现命令接口的后退命令实现类
private Role role;
public CommandBack(Role role){
this.role = role;
}
@Override
public void execute() {//控制游戏角色进行后退
role.back();
}
}
public class CommandAttack implements Command{//实现命令接口的攻击命令实现类
private Role role;
public CommandAttack(Role role){
this.role = role;
}
@Override
public void execute() {
role.attack();//控制游戏角色进行攻击
}
}
3.把游戏手柄做出来,游戏手柄就是掌握着所有命令引用的集合
public class Control {
private Command cm;//前进命令的对象
private Command cb;//后退命令的对象
private Command ca;//攻击命令的对象
public Control(Command cm,Command cb,Command ca){
this.cm = cm;
this.cb = cb;
this.ca = ca;
}
public void controlMarch(){//发送一条条的命令
cm.execute();
}
public void controlBack(){
cb.execute();
}
public void controlAttack(){
ca.execute();
}
}
4.玩家上场
public class Play {
public static void main(String[] args){
Role role = new Role();//对游戏角色进行实例化(命令的接收者)
Command cm = new CommandMarch(role);//把对游戏角色的引用传给前进命令
Command cb = new CommandBack(role);//把对游戏角色的引用传给后退命令
Command ca = new CommandAttack(role);//把对游戏角色的引用传给攻击命令
Control con = new Control(cm,cb,ca);//把对各种命令的引用传给游戏手柄
con.controlMarch();//操控游戏手柄进行各种操作
con.controlAttack();
con.controlAttack();
con.controlBack();
}
}
结果:
角色前进
角色攻击
角色攻击
角色后退
看到这或许你已经明白了,实际上我们是把本来看不见摸不着的虚拟的命令单独拿出来封装成一个对象,使之“实体化”了。
这样做有什么好处呢:
首先最大的好处我认为应该是便于命令的网络传输、持久化,假如我们用客户端对某台远程机器发起了一个命令,如果这条命令并未能及时执行,而且这时客户端更改了,迁移了,这个命令随之就不存在了,如果我们把命令单独拿出来作为一个对象,就可以对其序列化使之保留其状态,即使客户端更改了,也不影响这个命令后续的执行,也就是说每条命令自从发出来之后,就和客户端脱离了关系,可以作为一个独立的对象进行保存传输。
然后就是其对命令的细节进行封装,用户无需知道这条命令是怎么实现的,只需要知道有这些命令就可以了(知道游戏手柄有什么按钮并调用就可以了),减少使用难度。
再就是减少了客户端和最终命令接收者之间的耦合,可以更加方便的扩展命令。
缺点:
为了解耦增加了额外的角色,使用不当会让架构变得混乱难以维护。
如果只是几行简单的代码就能实现,用命令模式反而会增加工作量。
所以在请求——响应的场景中,使用命令模式最适合,但是具体要不要还是要看实际场景。