游戏设计模式-命令模式

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。

调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

(代码实现:C#)

一、什么是命令模式

        命令模式是一个高内聚的模式,其定义为:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能

  • Receiver接受者角色:该角色就是干活的角色,命令传递到这里是应该被执行的
  • Command命令角色:需要执行的所有命令都在这里声明
  • Invoker调用者角色:接收到命令,并执行命令(负责按照客户端的指令设置并执行命令,像命令的撤销,日志的记录等功能都要在此类中完成)

二、使用场景

1、当需要先将一个函数登记上,然后再以后调用此函数时,可以使用命令模式,其实这就是回调函数。
        有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。
        此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系

2、当某个或者某些操作需要支持撤销的场景

3、当要对操作过程记录日志,以便后期通过日志将操作过程重新做一遍时

三、使用方式

假设游戏中有两个Npc,其中NpcA只会跳舞,NpcB只会唱歌。此刻玩家可以在控制面板发送“看跳舞”、“听唱歌”指令,可以尝试使用命令模式

第一步、声明命令接口(Command)

public interface Command {
    public void Execute();
    // 如果需要撤销,可以加入Undo接口
    // private void Undo();
}

第二步、构建那些可以具体完成命令的角色(Receiver)

  • 构建NpcA:此时NpcA支持跳舞功能
public class NpcAReceiver {
    public void Dance(){
        Debug.Log("莫得感情的跳舞中……");
    }
}
  • 构建NpcB:此时NpcB支持唱歌功能
public class NpcBReceiver {
    public void Song(){
        Debug.Log("莫得感情的唱歌中……");
    }
}

第三步:构建各种具体命令(ConcreteCommand)

  • 构建一个跳舞命令,实现Command接口。因为只有NpcA提供这个功能,所以要在这个命令内部使用 NpcAReceiver 来具体执行。
public class DanceCommand : Command {
    private NpcAReceiver m_npcA;

    public DanceCommand(NpcAReceiver npc) {
        this.m_npcA = npc;
    }

    public void Execute() {
        m_npcA.Dance();
    }
}
  • 构建一个唱歌命令
public class SongCommand : Command {
    private NpcBReceiver m_npcB;

    public SongCommand(NpcBReceiver npc) {
        this.m_npcB = npc;
    }

    public void Execute() {
        m_npcB.Song();
    }
}

(如果命令执行还附带参数,例如时长、倍速等,那么具体的命令类里就不止包含具体执行命令的对象,还需要包含对应的参数数据)

第四步:构建命令的调用者 (Invoker)

public class NpcInvoker {
    // 也可以使用List存储命令,用栈的话方便回退
    private Stack<Command> m_commands = new Stack<Command>();

    public void ClearCommand(){
        m_commands.Clear();
    }
    
    public void ExcuteCommands(Command command) {
        command.Execute();
        m_commands.Push(command);
    }

    // 回退
    // public void UndoCommand() {
    //     Command command = m_commands.Pop();
    //     command.Undo();
    // }
}

第五步:客户端使用

public class GameClient {
    public void PlayWithNpc() {
        NpcInvoker npcInvoker = new NpcInvoker();

        DanceCommand danceCommand = new DanceCommand(new NpcAReceiver());
        SongCommand songCommand = new SongCommand(new NpcBReceiver());
        
        npcInvoker.ExcuteCommands(danceCommand);
        npcInvoker.ExcuteCommands(songCommand);
    }
}

四、使用要点

  • Command 接口非常简单,通常只有一个Execute方法,如果要支持撤销操作的话,再加一个Undo方法
  • 每个具体的命令类内部封装了实际执行命令的那个类(Recevier),以及执行需要的数据
  • 每个具体命令类只完成一个请求,有多少个请求就有多少个命令
  • Invoker类只认识接口Command,其他的都不认识
  • 客户端类负责生成命令,并通过Invoker组装执行。

五、优缺点

优点:
        - 将调用操作与具体执行者解耦
        - 添加一个命令非常容易
        - 很容易实现序列操作及实现回调系统
缺点:
        - 类太多,每次增加一个命令,就需要多加一个类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不伤欣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值