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