Blink

纸上得来终觉浅,绝知此事要躬行

设计模式:命令模式

  • 最近在项目中需要实现快捷键功能,通过快捷键打开游戏中一些界面,经过一些参考学习,使用命令模式实现了这个功能。写这篇博客也是为了简单记录一下自己实现思路,方便以后查看
  • 如果有什么地方不合理或者有错误,还请各位大佬留言指点一下,在下不胜感激

介绍

意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
应用实例:struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。
优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

命令模式结构图

《设计模式:命令模式》

例子:使用命令模式实现游戏快捷键

按键命令基类

public abstract class CommandBase
{
    // 命名的功能执行者(Receiver)
    protected InputController m_Controller = null;

    public CommandBase(InputController controller)
    {
        this.m_Controller = controller;
    }

    // 执行命令
    public abstract void Execute();
    // 撤消命令
    public abstract void Undo();
}

具体的命令:背包命令(打开/关闭)

public class BagCommand : CommandBase
{
    public BagCommand(InputController controller) : base(controller)
    {

    }

    public override void Execute()
    {
        m_Controller.OpenKnapsack();
    }

    // 撤消打开背包界面的实际操作是关闭背包界面,所以在这里的进行关闭背包界面
    public override void Undo()
    {
        m_Controller.CloseKnapsack();
    }
}

命令的执行者(Receiver)

using UnityEngine;

/// <summary>
/// Receiver(功能的执行者)
/// </summary>
public class InputController
{
    public void OpenKnapsack()
    {
        Debug.Log("打开背包");
    }

    public void CloseKnapsack()
    {
        Debug.Log("关闭背包");
    }
}

命令的管理者(Invoker)

using UnityEngine;

public class InputMgr : MonoBehaviour
{
    private InputController m_Controller = null;
    private CommandBase m_BagCommand = null;

    // 保存上一个输入的命令
    private CommandBase m_LastCommand = null;
    // 存储按键的数组(在实际的项目中可以实现玩家自定义快捷键)
    private string[] m_Keys = new string[] { "b" };

    private void Awake()
    {
        // 初始化
        m_Controller = new InputController();
        m_BagCommand = new BagCommand(m_Controller);
    }

    private void Update()
    {
        CommandBase cmd = InputHandle();
        if (cmd != null)
        {
            /**
             *  1.如果输入命令和上一个命令一样,则进行撤消操作
             *  2.否则就执行新的命令,并更新lastCommand
             */
            if (cmd == m_LastCommand)
            {
                cmd.Undo();
                m_LastCommand = null;
            }
            else
            {
                cmd.Execute();
                m_LastCommand = cmd;
            }
        }
    }

    private CommandBase InputHandle()
    {
        if (Input.GetKeyDown(m_Keys[0]))
        {
            return m_BagCommand;
        }

        // .....

        return null;
    }
}
  • 将InputMgr挂载到一个空物体上,按B键后会在控制台输入:打开背包,再次按B键会输出关闭背包
    《设计模式:命令模式》
点赞

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注