6.命令模式

6.行为型模式-命令模式


6.1 基础知识

学习难度:3

使用频率:4

总分:7

定义

命令模式(Command Pattern)将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及可撤销的操作。

说人话

封装不同的命令让接受者具体执行,调用者管理命令。

结构图

实现步骤

  • 接受者类:执行具体命令,封装了执行具体业务逻辑。
  • 命令接口:定义执行命令方法。
  • 多个具体命令类:实现命令接口,通常会定义接受者对象并在构造函数传入,并定义方法设置接受者对象。实现执行命令方法时通常会调用接受者封装好的具体业务逻辑。
  • 调用者类:负责让具体命令执行命令。通常会定义命令接口集合和管理命令接口集合的方法,例如添加命令方法和移除命令方法。定义执行命令的方法。实现执行命令方法时根据需要自行添加逻辑,调用命令接口集合中某些具体命令的执行命令方法。
  • 客户端:实例化接受者。实例化多个具体命令并传入接受者。实例化调用者并对需要的具体命令进行操作。调用调用者执行命令。

说明

命令模式的核心思想是将命令封装为对象,这样可以参数化客户对象,并将操作请求、排队请求或记录日志等操作。它允许请求的发送者和接收者彼此独立,不需要知道对方的存在,更加灵活,容易扩展。


6.2 模版代码

接受者类

// 命令接收者
public class Receiver
{
    public void Action()
    {
        Console.WriteLine("执行请求");
    }
}

命令接口和具体命令类

// 命令接口
public interface ICommand
{
    void Execute();
}

// 具体命令类
public class ConcreteCommand : ICommand
{
    private Receiver receiver; // 命令接收者

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

    public void Execute()
    {
        receiver.Action(); // 调用接收者的方法
    }
}

调用者类

// 命令调用者
public class Invoker
{
    private ICommand command; // 命令对象

    public void SetCommand(ICommand command)
    {
        this.command = command;
    }

    public void ExecuteCommand()
    {
        command.Execute(); // 执行命令
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        // 创建接收者
        Receiver receiver = new Receiver();

        // 创建具体命令并关联接收者
        ICommand command = new ConcreteCommand(receiver);

        // 创建调用者并设置命令
        Invoker invoker = new Invoker();
        invoker.SetCommand(command);

        // 调用命令
        invoker.ExecuteCommand(); //执行请求
    }
}

6.3 CSharp实践

实践需求

使用命令模式,模拟客户通过服务员下订单,服务员通知烤肉师傅执行订单。

烤肉师傅类

//烤肉师傅
public class Barbecuer
{
    // 烤肉者烤羊肉串
    public void BakeMutton()
    {
        Console.WriteLine("烤羊肉串");
    }

    // 烤肉者烤鸡翅
    public void BakeChickenWing()
    {
        Console.WriteLine("烤鸡翅");
    }
}

命令接口和具体命令类

// 命令接口
public interface ICommand
{
    void Execute();
}

// 烤羊肉串
public class BakeMuttonCommand : ICommand
{
    private Barbecuer barbecuer;

    public BakeMuttonCommand(Barbecuer barbecuer)
    {
        this.barbecuer = barbecuer;
    }

    public void Execute()
    {
        // 执行命令,让烤肉者烤羊肉串
        barbecuer.BakeMutton();
    }
}

// 烤鸡翅
public class BakeChickenWingCommand : ICommand
{
    private Barbecuer barbecuer;

    public BakeChickenWingCommand(Barbecuer barbecuer)
    {
        this.barbecuer = barbecuer;
    }

    public void Execute()
    {
        // 执行命令,让烤肉者烤鸡翅
        barbecuer.BakeChickenWing();
    }
}

服务员类

//服务员
public class Waiter
{
    private List<ICommand> commandList = new List<ICommand>();

    // 服务员接收订单
    public void SetOrder(ICommand ICommand)
    {
        commandList.Add(ICommand);
    }

    // 服务员取消订单
    public void CancelOrder(ICommand ICommand)
    {
        commandList.Remove(ICommand); // 从订单中移除命令
    }

    // 服务员通知烤肉者执行订单
    public void Notify()
    {
        Console.WriteLine("服务员通知烤肉师傅做订单");
        foreach (var command in commandList)
        {
            command.Execute();
        }
        commandList.Clear();//执行完的命令就清空
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        Barbecuer barbecuer = new Barbecuer();
        Waiter waiter = new Waiter();

        ICommand muttonCommand = new BakeMuttonCommand(barbecuer);
        ICommand chickenWingCommand = new BakeChickenWingCommand(barbecuer);

        // 客户下单
        waiter.SetOrder(muttonCommand); // 添加羊肉串订单
        waiter.SetOrder(chickenWingCommand); // 添加鸡翅订单
        waiter.Notify(); // 服务员通知烤肉者执行订单
        // 服务员通知烤肉师傅做订单
        // 烤羊肉串
        // 烤鸡翅
        
        // 客户取消订单
        waiter.SetOrder(muttonCommand); // 添加羊肉串订单
        waiter.SetOrder(chickenWingCommand); // 添加鸡翅订单
        waiter.CancelOrder(muttonCommand); // 取消羊肉串订单
        waiter.Notify(); // 服务员通知烤肉者执行订单(不再包括取消的订单)
        // 服务员通知烤肉师傅做订单
        // 烤鸡翅
    }
}

6.4 Unity实践

实践需求

使用命令模式,实现按下对薇恩默认,跑步,攻击状态的切换。

薇恩命令接收者类

// 薇恩命令接收者类,负责执行具体的操作
public class VayneReceiver
{
    public GameObject vayneGameObject; // 薇恩游戏对象
    public Animator vayneAnimator; // 薇恩的动画控制器

    private static readonly int Run = Animator.StringToHash("run"); // animator 中的跑步参数名
    private static readonly int Attack = Animator.StringToHash("attack"); // animator 中的攻击参数名

    public VayneReceiver(GameObject vayneGameObject)
    {
        this.vayneGameObject = vayneGameObject;
        var gameObject = this.vayneGameObject;
        if (gameObject != null)
            vayneAnimator = gameObject.GetComponent<Animator>(); // 获取薇恩游戏对象上的动画控制器组件
    }

    // 默认状态,停止所有行为
    public void IdleState()
    {
        Debug.Log("进入默认状态");
        vayneAnimator.SetBool(Run, false); // 将跑步参数设置为 false,停止跑步动画
        vayneAnimator.SetBool(Attack, false); // 将攻击参数设置为 false,停止攻击动画
    }

    // 跑步状态
    public void RunState()
    {
        Debug.Log("进入跑步状态");
        vayneAnimator.SetBool(Run, true); // 将跑步参数设置为 true,开始播放跑步动画
        vayneAnimator.SetBool(Attack, false); // 将攻击参数设置为 false,停止攻击动画
    }

    // 攻击状态
    public void AttackState()
    {
        Debug.Log("进入攻击状态");
        vayneAnimator.SetBool(Attack, true); // 将攻击参数设置为 true,开始播放攻击动画
    }
}

薇恩命令接口和薇恩具体命令类

// 薇恩命令接口
public interface IVayneCommand
{
    void ExecuteCommand(); // 执行命令的方法
}

// 默认状态命令
public class IdleVayneCommand : IVayneCommand
{
    public VayneReceiver vayneReceiver; // 薇恩命令接收者

    public IdleVayneCommand(VayneReceiver vayneReceiver)
    {
        this.vayneReceiver = vayneReceiver;
    }

    // 执行默认状态命令,调用接收者对象的 IdleState 方法
    public void ExecuteCommand()
    {
        vayneReceiver.IdleState();
    }
}

// 跑步状态命令
public class RunVayneCommand : IVayneCommand
{
    public VayneReceiver vayneReceiver; // 薇恩命令接收者

    public RunVayneCommand(VayneReceiver vayneReceiver)
    {
        this.vayneReceiver = vayneReceiver;
    }

    // 执行跑步状态命令,调用接收者对象的 RunState 方法
    public void ExecuteCommand()
    {
        vayneReceiver.RunState();
    }
}

// 攻击状态命令
public class AttackVayneCommand : IVayneCommand
{
    public VayneReceiver vayneReceiver; // 薇恩命令接收者

    public AttackVayneCommand(VayneReceiver vayneReceiver)
    {
        this.vayneReceiver = vayneReceiver;
    }

    // 执行攻击状态命令,调用接收者对象的 AttackState 方法
    public void ExecuteCommand()
    {
        vayneReceiver.AttackState();
    }
}

薇恩命令调用者类

// 薇恩命令调用者类
public class VayneInvoker : MonoBehaviour
{
    //按键命令映射字典
    private Dictionary<KeyCode, IVayneCommand> vayneCommandDictionary = new Dictionary<KeyCode, IVayneCommand>();

    // 添加薇恩命令到字典中
    public void AddVayneCommand(KeyCode keyCode, IVayneCommand vayneCommand)
    {
        // 检查是否已经包含该按键的命令,如果是则替换,否则添加新命令
        if (vayneCommandDictionary.ContainsKey(keyCode))
        {
            vayneCommandDictionary[keyCode] = vayneCommand;
        }
        else
        {
            vayneCommandDictionary.Add(keyCode, vayneCommand);
        }
    }

    private void Update()
    {
        // 在每一帧检查输入
        CheckInput();
    }

    // 检查输入的按键,并执行对应的命令
    private void CheckInput()
    {
        foreach (var nowCommand in vayneCommandDictionary)
        {
            // 如果玩家按下了与命令相关的按键
            if (Input.GetKeyDown(nowCommand.Key))
            {
                nowCommand.Value.ExecuteCommand(); // 执行命令
            }
        }
    }
}

客户端

public class TestCommandPattern : MonoBehaviour
{
    private void Start()
    {
        // 加载出 Vayne 游戏对象
        GameObject vayneGameObject = Instantiate(Resources.Load<GameObject>("Lesson24_行为型模式_命令模式/Vayne"));

        // 创建 Vayne 接收者对象
        VayneReceiver vayneReceiver = new VayneReceiver(vayneGameObject);

        // 创建各种不同的薇恩命令对象
        IdleVayneCommand idleVayneCommand = new IdleVayneCommand(vayneReceiver);
        RunVayneCommand runVayneCommand = new RunVayneCommand(vayneReceiver);
        AttackVayneCommand attackVayneCommand = new AttackVayneCommand(vayneReceiver);

        // 将命令添加到调用者对象中
        VayneInvoker vayneInvoker = vayneGameObject.AddComponent<VayneInvoker>();
        vayneInvoker.AddVayneCommand(KeyCode.Space, idleVayneCommand);
        vayneInvoker.AddVayneCommand(KeyCode.W, runVayneCommand);
        vayneInvoker.AddVayneCommand(KeyCode.A, runVayneCommand);
        vayneInvoker.AddVayneCommand(KeyCode.S, runVayneCommand);
        vayneInvoker.AddVayneCommand(KeyCode.D, runVayneCommand);
        vayneInvoker.AddVayneCommand(KeyCode.LeftShift, attackVayneCommand);
        vayneInvoker.AddVayneCommand(KeyCode.RightShift, attackVayneCommand);
    }
}

运行结果





转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏