暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

设计模式系列篇(二十)——命令模式

复旦猿 2020-09-10
637

What

命令模式(Command Design Pattern),将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。

Why

命令模式具有以下优点:

  1. 降低系统的耦合度。

  2. 新的命令可以很容易地加入到系统中。

  3. 可以比较容易地设计一个命令队列和宏命令(组合命令)。

  4. 可以方便地实现对请求的Undo和Redo。

When

在以下情况下可以使用命令模式:

  1. 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。

  2. 系统需要在不同的时间指定请求、将请求排队和执行请求。

  3. 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

  4. 系统需要将一组操作组合在一起,即支持宏命令。

How

命令模式包含如下角色:

  • Command类,即所有命令类的接口类,定义接口方法execute()。

  • ConcreteCommand类,即具体的命令类,如移动类(MoveCommand、SkillCommand),实现execute方法。

  • Invoker类,负责调用命令对象执行请求,相关的方法叫做action(行动)方法。

  • Reciever类,负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做action(行动)方法。

  • Client类,创建一个具体命令ConcreteCommand对象并确定其接收者。

命令模式UML图

今天,我们就来实现一个《王者荣耀》简陋版,主要实现客户端发送的指令(英雄的移动和放大招都可以抽象为一个指令)发送至消息队列中,然后服务端从消息队列中取出命令进行执行。

首先,我们定义Command接口及其实现类:

public interface Command {
void execute();
}

public class MoveCommand implements Command {
/**
* target object
*/

private Receiver receiver;

/**
* target loction
*/

private double locX;
private double locY;

public MoveCommand(Receiver receiver, double locX, double locY) {
this.receiver = receiver;
this.locX = locX;
this.locY = locY;
}

@Override
public void execute() {
this.receiver.action();
System.out.println(String.format("Move to (%f, %f)", locX, locY));
}
}

public class SkillCommand implements Command {
private Receiver receiver;

public SkillCommand(Receiver receiver) {
this.receiver = receiver;
}

@Override
public void execute() {
receiver.action();
System.out.println(String.format("use skill: %s", receiver.getSkill()));
}
}

然后,实现接受对象类,内部的action方法执行真正的操作。

public class Receiver {
private String name;
private String skill;

public Receiver(String name, String skill) {
this.name = name;
this.skill = skill;
}

public String getName() {
return name;
}

public String getSkill() {
return skill;
}

public void action() {
System.out.println(String.format("%s do action", this.getName()));
}
}

接下来,定义调用类:

public class Invoker {
/**
* command obejct
*/

private Command command;

public Invoker(Command command) {
this.command = command;
}

/**
* action
*/

public void action() {
command.execute();
}
}

最后,来个测试类,即客户端类。

public class TestMain {
public static void main(String[] args) {
// create receiver object
Receiver receiver = new Receiver("Arthur", "回旋打击");

// create the command object, and set the receiver object
Command command1 = new MoveCommand(receiver, 10.0, 20.0);
Command command2 = new SkillCommand(receiver);
Command command3 = new MoveCommand(receiver, 30.0, 30.0);
Command command4 = new SkillCommand(receiver);
Command command5 = new MoveCommand(receiver, 40.0, 40.0);
Command command6 = new SkillCommand(receiver);

// put the command to the queue
Queue<Command> queue = new LinkedList<>();
queue.offer(command1);
queue.offer(command2);
queue.offer(command3);
queue.offer(command4);
queue.offer(command5);
queue.offer(command6);

// get the command and create the invoker to action
while (!queue.isEmpty()) {
Command command = queue.poll();
Invoker invoker = new Invoker(command);
invoker.action();
System.out.println();
}
}
}

输出如下:

Arthur do action
Move to (10.000000, 20.000000)

Arthur do action
use skill: 回旋打击

Arthur do action
Move to (30.000000, 30.000000)

Arthur do action
use skill: 回旋打击

Arthur do action
Move to (40.000000, 40.000000)

大功告成!

代码地址

i-learning

写在最后

如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~


文章转载自复旦猿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论