命令模式的原理解读

命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。

命令模式的实战讲解

整个手游后端服务器轮询获取客户端发来的请求,获取到请求之后,借助命令模式,把请求包含的数据和处理逻辑封装为命令对象,并存储在内存队列中。然后,再从队列中取出一定数量的命令来执行。执行完成之后,再重新开始新的一轮轮询。

  1. public interface Command {
  2. void execute();
  3. }
  4. public class GotDiamondCommand implements Command {
  5. // 省略成员变量
  6. public GotDiamondCommand(/*数据*/) {
  7. //...
  8. }
  9. @Override
  10. public void execute() {
  11. // 执行相应的逻辑
  12. }
  13. }
  14. //GotStartCommand/HitObstacleCommand/ArchiveCommand类省略
  15. public class GameApplication {
  16. private static final int MAX_HANDLED_REQ_COUNT_PER_LOOP = 100;
  17. private Queue<Command> queue = new LinkedList<>();
  18. public void mainloop() {
  19. while (true) {
  20. List<Request> requests = new ArrayList<>();
  21. //省略从epoll或者select中获取数据,并封装成Request的逻辑,
  22. //注意设置超时时间,如果很长时间没有接收到请求,就继续下面的逻辑处理。
  23. for (Request request : requests) {
  24. Event event = request.getEvent();
  25. Command command = null;
  26. if (event.equals(Event.GOT_DIAMOND)) {
  27. command = new GotDiamondCommand(/*数据*/);
  28. } else if (event.equals(Event.GOT_STAR)) {
  29. command = new GotStartCommand(/*数据*/);
  30. } else if (event.equals(Event.HIT_OBSTACLE)) {
  31. command = new HitObstacleCommand(/*数据*/);
  32. } else if (event.equals(Event.ARCHIVE)) {
  33. command = new ArchiveCommand(/*数据*/);
  34. } // ...一堆else if...
  35. queue.add(command);
  36. }
  37. int handledCount = 0;
  38. while (handledCount < MAX_HANDLED_REQ_COUNT_PER_LOOP) {
  39. if (queue.isEmpty()) {
  40. break;
  41. }
  42. Command command = queue.poll();
  43. command.execute();
  44. }
  45. }
  46. }
  47. }

命令模式 VS 策略模式

实际上,每个设计模式都应该由两部分组成:

  • 第一部分是应用场景,即这个模式可以解决哪类问题;
  • 第二部分是解决方案,即这个模式的设计思路和具体的代码实现。不过,代码实现并不是模式必须包含的。如果你单纯地只关注解决方案这一部分,甚至只关注代码实现,就会产生大部分模式看起来都很相似的错觉。

实际上,设计模式之间的主要区别还是在于设计意图,也就是应用场景。单纯地看设计思路或者代码实现,有些模式确实很相似,比如策略模式和工厂模式。

在策略模式中,不同的策略具有相同的目的、不同的实现、互相之间可以替换。比如,BubbleSort、SelectionSort 都是为了实现排序的,只不过一个是用冒泡排序算法来实现的,另一个是用选择排序算法来实现的。而在命令模式中,不同的命令具有不同的目的,对应不同的处理逻辑,并且互相之间不可替换。