1.命令模式简述
“讲一个请求(request)封装成一个对象,从而允许你使用不同的请求、队列或日志将客户端参数化,同时支持请求操作的撤销与恢复”以上是GoF对于命令模式进行具有预见性的深奥描述。《游戏编程模式》一书中,命令模式的解释是:命令就是一个对象化(实例化)的方法调用。(A command is a reified method call)。看了以上两段描述,如果你明白了什么是命令模式,恭喜你是天才~~如果没太明白,那么下面我将按我的理解介绍一下什么是命令模式。
所谓命令模式,其实就是将一个功能函数(或者说是一次行为)进行一次封装,从而去方便地实现对于功能函数(这一次行为)进行额外的操作。
2.运用场景
举个例子,现在有一款游戏q,w,e三个按钮控制角色跳跃,闪避,蹲下(别问我什么游戏这样设计按键,我不知道)。
public void HandleInput() { if(Input.GetKey(KeyCode.Q)){ Jump(); } else if(Input.GetKey(KeyCode.W)){ dodge(); } else if(Input.GetKey(KeyCode.E)){ squat(); } }
功能做好了,现在玩家想要去修改按键,变成w,q,e去控制玩家跳跃,闪避蹲下。这个时候就遇到麻烦了。现在比较好的处理方案就是将单个函数包装为一个命令类,再去做映射。
class Commod{ public Commod(){} virtual public Execute(){} } class JumpClass : Commod{ public JumpClass():base(){} Override public Execute{ Jump(); } } Class InoutHandler{ privte Commod botton1; privte Commod botton2; privte Commod botton3; public void HandleInput() { if(Input.GetKey(KeyCode.Q)){ button1.Execute(); } else if(Input.GetKey(KeyCode.W)){ button2.Execute(); } else if(Input.GetKey(KeyCode.E)){ button3.Execute(); } } }
好了现在问题得到了完美的解决。
3.拓展
通过以上的例子,什么是命令模式,已经有了一定了解了。现在我们思维拓展开来,当一个功能函数(其实,我更愿意将其称之为一个“行为”)改为一个类之后,可以去做的事情就很多了。
例如,现在游戏地图因为太大,需要分场景去加载。应该只加载出视野所能看到的部分再大一些的区域。这时只需要根据当前角色坐标去得出哪些场景需要卸载哪些场景需要加载,貌似问题就解决了。
但是这时有一个问题,就是玩家如果在触发加载、卸载逻辑的分界线处反复运动,那么连续的加载、卸载会浪费性能。这时,我们就应该使用命令模式,将卸载场景这一操作包装起来,存储到一个队列当中,进行一个延迟执行,几秒钟之内,再次触发加载该场景时,就取消这次卸载操作,如若没有再次触发加载场景行为,则会真正执行卸载逻辑。
从这个例子可以看出,如果你希望对于某个操作有额外附加操作,都可以使用命令模式,来非常快速的实现。
4.重量级还是轻量级
总是将一个函数包装为一个类去实现该模式,老实说,有一些“重量级”了。更“轻量级”的方式是,将函数去用闭包的方式包装起来(c#中只需要实现函数的返回值是委托类型,就可以轻易实现闭包了),这样函数依旧是函数而不是类,也可以去做到行为额外的管理和操作。
具体选择使用闭包还是类,主要根据需求来确定,如果你只需要简易的进行操作的排序和计数等功能,那么闭包是更好的。如果你需要更复杂的操作,例如撤回以完成的操作,或者操作本身引用外部进行复杂判断等,那么类的方式就会更好。