命令模式

一、概述

最近接触公司一个模块,是公司的首战模块.它其中用的就是命令模式,通过读表创建一个命令队列,然后依次执行.之前没有了解过,一直不知道这个模块是什么内容,最近灵光一现,才发现原来是命令模式.所以写篇博客记录一下.

命令模式是一种常用的行为型设计模式,它将请求发送者与请求接收者解耦.请求发送者通过命令对象来间接引用接收者.使用系统具有更好灵活性,再可以不修改现有系统源代码的情况下让相同的发送者对应不同的接收者.

命令模式的定义比较复杂,有很多术语.如"用不同的请求对客户进行参数化","对请求排队""对请求进行排队等".

命令模式在我看来最主要的地方就是执行,命令者(领导)向接受命令者(员工)发送命令,然后接受命令者执行命令,但大部分命令模式都会和队列结合在一起.命令者发送命令,然后接受命令者依次执行命令.

二、命令模式结构与实现

2.1 结构

命令模式的结构核心在于引用了抽象命令类和具体命令类,发送者只要指定一个命令对象,再通过命令对象来调用请求接受者的处理方法.其结构如图所示

命令模式一般包括4个角色

  • Command(抽象命令模式):抽象命令类一般是一个抽象类或接口类,在其中声明了用于执行请求的execute等方法,通过execute方法可以调用请求接受者的相关操作.
  • ConcreteComman(具体命令类):具体命令类是抽象类命令类的子类,实现了抽象命令类的具体声明方法.具体命令类在实现execute方法时调用接受者对象的相关操作.
  • Invoker(调用者):调用者即请求发送者,他通过命令对象来执行请求.
  • Receiver(接收者):接受者执行与请求相关的操作,具体实现对请求的业务处理.

2.2 命令队列

在实际应用中,命令模式总是以命令队列出现,一般与工厂模式结合使用,调用者从工厂模式中获取相应的命令,然后以队列的形式依次发送命令.命令队列就像批处理一样,对一组命令对象进行批处理.

2.3 命令模式(队列)实现

2.3.1 抽象命令类实现

namespace CommandMode
{
    public abstract class CommandBase
    {
        private string[] _parameters;
        private Action _completeCallBack;
        private int _taskId = 0;
        private CommandStatus _status;
        public CommandBase(string[] parameters, Action completeCallBack, int taskId)
        {
            _parameters = parameters;
            _completeCallBack = completeCallBack;
            _taskId = taskId;
            _status = CommandStatus.Idle;
            OnInit();
        }
        //获得参数
        public string GetParameter(int index)
        {
            if (index < 0 || _parameters.Length <= index) return "";
            return _parameters[index];
        }

        public string[] GetParameters()
        {
            return _parameters;
        }

        //指令是否可执行
        public bool IsExecutable()
        {
            return true;
        }
        // 获得指令当前状态
        public CommandStatus GetCommandState()
        {
            return _status;
        }
        // 准备
        public void Prepare()
        {
            if (!ChangeStatusChangeAvailable(CommandStatus.Preparing)) return;
            _status = CommandStatus.Preparing;
            OnPrepare();
            StartExecute();
        }
        //开始执行
        private void StartExecute() 
        {
            if (IsExecutable()) return;
            Execute();
        }
        //执行
        public void Execute()
        {
            if (!ChangeStatusChangeAvailable(CommandStatus.Executing)) return;
            _status = CommandStatus.Executing;
            OnExecute();
        }
        //取消
        public void Cancle()
        {
            if (!ChangeStatusChangeAvailable(CommandStatus.Canle)) return;
            _status = CommandStatus.Canle;
            OnCancel();
        }
        //完成
        public void Complete()
        {
            if (!ChangeStatusChangeAvailable(CommandStatus.Compelete)) return;
            _status = CommandStatus.Compelete;
            _completeCallBack?.Invoke();
            OnComplete();
        }
        //错误
        public void Error()
        {
            if (!ChangeStatusChangeAvailable(CommandStatus.Error)) return;
            _status = CommandStatus.Error;
            OnError();
        }
        //销毁
        public void Dispose()
        {
            if (!ChangeStatusChangeAvailable(CommandStatus.Disposed)) return;
            _status = CommandStatus.Disposed;
            OnDispose();
            _parameters = null;
            _completeCallBack = null;
        }
        public bool IsExecuting()
        {
            return _status == CommandStatus.Executing;
        }

        public bool ChangeStatusChangeAvailable(CommandStatus status)
        {
            if (_status == CommandStatus.Disposed) return false;
            return _status != status;
        }

        public abstract void OnInit();
        public abstract void OnPrepare();
        public abstract void OnExecute();
        public abstract void OnCancel();
        public abstract void OnComplete();
        public abstract void OnError();
        public abstract void OnDispose();
    }

    public enum CommandStatus
    {
        Error       = 0, //出错
        Idle        = 1, //闲置
        Preparing   = 2, //准备执行
        Executing   = 3, //执行中
        Canle       = 4, //取消
        Compelete   = 5, //完成
        Disposed    = 6, //已销毁
    }
}

2.3.2 具体命令类实现

namespace CommandMode
{
    public class ConcreteCommand : CommandBase
    {
        private int _param = 0;
        public override void OnInit()
        {
            _param = GetIntParameter(1);
        }

        public override void OnPrepare()
        {
        }

        public override void OnExecute()
        {

        }

        public override void OnCancel()
        {

        }

        public override void OnComplete()
        {

        }

        public override void OnDispose()
        {

        }

        public override void OnError()
        {

        }
    }
}

namespace CommandMode
{
    internal class DefaultCommand : CommandBase
    {
        public override void OnComplete()
        {
            base.OnComplete();
        }
    }
}

2.3.3 命令工厂类实现

namespace CommandMode
{
    public static class CommandFactory
    {
        public static CommandBase CreateCommandExecutor(CommandExecuteName executeName, Action completeCallback, int taskId, string[] stringParams = null, int[] intParams = null)
        {
            CommandBase command = null;
            switch (executeName)
            {
                case CommandExecuteName.ConcreteCommand:
                    command = new ConcreteCommand();
                    break;
                default:
                    command = new DefaultCommand();
                    break;
            }
            command.Init(completeCallback, taskId, stringParams, intParams);
            return command;
        }
    }

    public enum CommandExecuteName
    {
        ConcreteCommand = 0,
    }
}

2.3.4 命令队列类实现

using System.Threading.Tasks;

namespace CommandMode
{
    internal class CommandSequence
    {
        private bool _isComplete = false;
        private Action _completeCallBack;
        private List<CommandBase> _commandList;

        public CommandSequence(CommonConfig commonConfig,  Action completeCallBack) 
        {
            _completeCallBack = completeCallBack;
            _commandList = new List<CommandBase>();
            CreateCommandList(commonConfig);
        }

        public void CreateCommandList(CommonConfig commonConfig)
        {
            if(commonConfig == null || commonConfig.CommonDatas == null || commonConfig.CommonDatas.Length == 0)
            {
                OnComplete();
                return;
            }
            foreach(var commandData in commonConfig.CommonDatas)
            {
                CommandExecuteName executeName = (CommandExecuteName)commandData.CommandName;
                var taskId = commandData.TaskId;
                var intParams = commandData.IntPramas;
                var stringParams = commandData.StringPramas;
                var command = CommandFactory.CreateCommandExecutor(executeName, OnCurCommandComplete, taskId, stringParams, intParams);
                _commandList.Add(command);
            }
        }
        public bool IsComplete() 
        {
            return _isComplete;
        }
        public bool IsExecuted()
        {
            bool res = false;
            foreach (var command in _commandList)
            {
                var status = command.GetCommandState();
                if (status != CommandStatus.Idle && status != CommandStatus.Preparing) 
                {
                    res = true;
                    break;
                }
            }
            return res; 
        }

        public void Execute()
        {
            if (_commandList == null || _commandList.Count == 0)
            {
                OnComplete();
                return;
            }
            bool isComplete = true;
            foreach (var command in _commandList)
            {
                var state = command.GetCommandState();
                if (state == CommandStatus.Idle)
                {
                    command.Prepare();
                    isComplete = false;
                    break;
                }
                else if (state == CommandStatus.Compelete)
                {

                }
                else
                {
                    isComplete = false;
                    break;
                }
            }
            if (isComplete) { OnComplete(); }
        }
        //当前指令执行完毕
        private void OnCurCommandComplete()
        {
            Execute();
        }

        private void OnComplete()
        {
            _isComplete = true;
            _completeCallBack?.Invoke();
        }
        public void Dispose()
        {
            foreach (var command in _commandList)
            {
                command.Dispose();
            }
            _commandList?.Clear();
            _commandList = null;
        }
    }
    public class CommonConfig
    {
        public CommonData[] CommonDatas;
    }
    public class CommonData
    {
        public int TaskId;
        public int CommandName;
        public int[] IntPramas;
        public string[] StringPramas;
    }
}

三、命令模式的优/缺点与使用环境

3.1 优点

  • 降低系统耦合度:由于请求者和接受者之间不存在直接引用,因此请求者与接受者两者之间实现完全解耦,使两者之间具有良好的独立性
  • 热插拔:可以比较容易的实现一个命令队列,新的命令也很容易加入到系统中,新增的命令也不会影响到其他类,满足开闭原则

3.2 缺点

使用命令模式可能会导致系统有过多的具体命令类,针对每一个请求接收器的调用操作都需要设计一个具体命令类,导致代码中存在很多命令类.

3.3 使用环境

系统需要在不同的时间指定请求、将请求排队和执行请求,可以很方便地去使用命令模式.