设计模式系列篇

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

2020-09-10  本文已影响0人  复旦猿

What

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

Why

命令模式具有以下优点:

  1. 降低系统的耦合度。
  2. 新的命令可以很容易地加入到系统中。
  3. 可以比较容易地设计一个命令队列和宏命令(组合命令)。
  4. 可以方便地实现对请求的Undo和Redo。

When

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

  1. 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  2. 系统需要在不同的时间指定请求、将请求排队和执行请求。
  3. 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  4. 系统需要将一组操作组合在一起,即支持宏命令。

How

命令模式包含如下角色:

命令模式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

写在最后

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

上一篇 下一篇

猜你喜欢

热点阅读