65.9K
CodeProject 正在变化。 阅读更多。
Home

将类转换为 CommandHandler

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (9投票s)

2010 年 7 月 13 日

CPOL

11分钟阅读

viewsIcon

23808

downloadIcon

194

一篇关于使用命令设计模式创建命令处理框架的文章。

commandhandler_2.png

引言

本文旨在通过一个通用、可扩展的概念来让类响应客户端发出的命令。该概念围绕 CommandHandler,它处理具有配置信息的 Command,并处理 CommandHandlerRoutine 所需的特定数据,而 CommandHandlerRoutine 则由 CommandHandler 实现。我们还强调了保持 CommandHandler、Command 和 Data 的通用性、可扩展性和灵活性。本文分为以下几个部分:

  1. 动机
  2. 该概念的应用场景
  3. 概念解释
  4. 实现该概念的主要组件
  5. 整体类结构设计
  6. 类及其成员的解释
  7. 如何创建 ActualCommandHandler
  8. 客户端如何向 ActualCommandHandler 发送命令

1. 动机

在我日常的开发工作中,有一个需求,我需要某种东西能够响应从外部发出的命令,可能是来自代码的其他部分或其他子模块。我认为这种东西应该是一个类,因为类可以保存必要的数据,提供适当的封装,并可以作为该需求的抽象。因此,我尝试将一个类转换为一个 CommandHandler,使其能够有效地处理/响应其他实体的调用/命令。

2. 该概念的应用场景

此概念可用于开发服务器代码,因为服务器响应客户端的调用/命令。此外,本文所述的类结构可以在实现任何客户端-服务器体系结构中发挥作用。

3. 概念解释

在讨论本文所述概念时,有两个常用术语:

  1. CommandHandler:这将是应该遵循本文所述规范的类,以便成为 CommandHandler 类。
  2. 客户端(们):所有将利用 CommandHandler 或向 CommandHandler 发送命令的其他代码。

CommandHandler 主要在多线程环境中得到利用,其中所有客户端将持续发送命令。CommandHandler 可以通过两种方式响应命令:

  1. SendCommand:在这种方式下,客户端将命令发送给 CommandHandler,如果 CommandHandler 空闲,则命令会很快得到响应/执行。如果 CommandHandler 正在忙于处理其他命令,那么客户端需要等到 CommandHandler 响应/执行完命令。在 CommandHandler 中,直到先前发送的命令完全执行完毕,客户端才能继续处理或发送另一个命令。
  2. PostCommand:在 PostCommand 的情况下,客户端无需等待命令执行完成。在 CommandHandler 中执行先前发布的命令时,它可以继续处理或发送另一个命令。

这样,客户端发出的命令首先存储在 CommandHandler 中的队列里。CommandHandler 从队列中获取命令,并在单独的线程中逐个处理它们。根据优先级,命令被临时存储在高、中或低优先级命令队列之一中。命令根据其优先级执行。因此,所有高优先级命令将在中优先级命令之前执行,而中优先级命令将在低优先级命令之前执行。顺便说一下,命令是从后端插入队列,并从前端获取或移除以进行处理。这符合标准的队列概念。

在 SendCommand 和 PostCommand 的情况下,CommandHandler 内部的主要过程/方法,它

  • 从队列中获取并移除命令,
  • 处理获取到的命令,
  • 通知客户端命令执行完成。

在一个单独的线程中运行。这个主方法是 CXCommandHandler::CommandProcessor。因此,在将一个类转换为 CommandHandler 后,这个转换后的类充当了一个可以持续执行命令的工作线程。

4. 实现该概念的主要组件

  1. 模板 <typename ACTUALCOMMANDHANDLER>CXCommandHandler
  2. CXCommand
  3. CXData
  4. CActualCommandHandler - 它可以是任何需要转换为 CommandHandler 的类
    • COMMAND
      • 枚举 COMMANDLIST
      • 结构体 DATA_COMMANDHANDLERROUTINE1
      • 结构体 DATA_COMMANDHANDLERROUTINE2
      • 结构体 DATA_COMMAND..

5. 整体类结构设计

commandhandler_1.png

这个类结构实现在 "CommandHandler.h" 和 "CommandHandler.cpp" 文件中。为了实现这个概念,使用了命令设计模式。客户端创建命令(CXCommand 类的实例),用必要的数据(派生自 CXData 的类的实例)填充它,然后将其发送给 CommandHandler(派生自 CXCommandHandler 类的类的实例),由 CommandHandler 执行这些命令。

  1. 模板 <typename ACTUALCOMMANDHANDLER>CXCommandHandler:这是整体概念的主要组成部分。这是一个模板类,将作为您希望转换为 CommandHandler 的类的基类。此类提供并实现了实现 CommandHandler 所需的所有必要功能。
  2. CXCommand:这是客户端和 CommandHandler 之间通信的媒介。此类提供了必要的成员,客户端可以通过这些成员设置命令优先级、命令 ID 等信息,以及执行命令所需的数据。
  3. CXData:此类作为执行单个命令所用数据的基类。
  4. CActualCommandHandler:这是您希望转换为 CommandHandler 的类。此类应派生自 CXCommandHandler 类。此类的主要职责是为客户端将要发出的命令实现 CommandHandlerRoutines。
    • COMMAND:此类主要充当命名空间,用于保存与 CActualCommandHandler 将要处理的所有命令相关的所有内容。此类声明在 CActualCommandHandler 内部。此类定义了两个重要项,客户端可以通过它们来填充命令。
      • enum COMMANDLISTCActualCommandHandler 实现的每个 CommandHandlerRoutine 都被赋予一个唯一的 ID。COMMANDLIST 存储这些 CommandIDs。
      • struct DATA_COMMANDHANDLERROUTINE1:此结构体派生自 CXData 类。此结构体指定了与 CActualCommandHandler 实现的相关 CommandHandlerRoutine1 所使用的数据。
      • struct DATA_COMMANDHANDLERROUTINE2:此结构体派生自 CXData 类。此结构体指定了与 CActualCommandHandler 实现的相关 CommandHandlerRoutine2 所使用的数据。
      • 结构体 DATA_COMMAND..

注意:CommandHandlerRoutines 应严格遵循以下签名

int CommandHandlerRoutineName(CXData *pdata);

6. 类及其成员的解释

这可以直接在源文件中找到。

7. 如何创建 ActualCommandHandler

考虑一个类 CAccountService。我们将把这个类转换为一个 ActualCommandHandler,它将能够响应客户端(其他代码)发出的各种命令。客户端代码可以向其发送命令以

  • 注册/注销 CAccountService 所提供服务的用户。
  • 向用户发送他们的账户信息邮件。
  • 打印用户的账户信息。

总的来说,CAccountService 应该能够处理所有现有命令,并且能够扩展以处理未来可能出现的其他类型的命令。

现在,请按以下步骤操作:

  1. 创建两个文件:AccountService.hAccountService.cpp
  2. 包含现有的类结构
  3. //AccountService.h file
    #include "CommandHandler.h"
  4. 创建一个类 CAccountService 并将其派生自 CXCommandHandler
  5. //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    };
  6. 将现有的宏 IMPLEMENT_GETTHISMEMBERFUNCTION 放在 CAccountService 类的 public 部分。此宏可以在 CommandHandler.h 文件中找到。
  7. //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    };
  8. 现在,在 CAccountService 的 public 部分创建一个类 COMMANDCOMMAND 将充当命名空间,并保存与 CAccountService 将要处理的所有命令相关的详细信息。请参考 5. 整体类结构设计 以获取关于 COMMAND 类的解释。
  9. //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        };
    };
  10. 现在,假设这个类 CAccountService 提供了一些服务,客户端可以通过发送相应命令来访问这些服务。我们需要实现这些服务,提供称为 CommandHandlerRoutines 的例程。因此,下一步是实现 CommandHandlerRoutines。

实现 CommandHandlerRoutines 需要以下步骤:

  1. 声明 CommandHandlerRoutine
  2. //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        };
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    如您所见,CommandHandlerRoutine Register 以一种非常通用的形式接受参数。这个参数是指向 CXData 类的指针,它实际上将指向 Register CommandHandlerRoutine 将操作的特定数据。

    例如,Register CommandHandlerRoutine 需要一个 struct CAccountService::COMMAND::DATA_USERINFO 的实例。请参考 5. 整体类结构设计 以获取与 CommandHandlerRoutine 数据需求相关的解释。

  3. 声明 CommandHandlerRoutine 所需的数据。
  4. 上面步骤中声明的 Register CommandHandlerRoutine 需要一个 CAccountService::COMMAND::DATA_USERINFO 的实例。因此,在充当命名空间的 CAccountService::COMMAND 类的 public 部分创建一个 struct DATA_USERINFO

    //AccountService.h file
    
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    此结构体包含 Register CommandHandlerRoutine 用于注册用户所需的数据。

  5. 声明代表 CommandHandlerRoutine 的 CommandID。
  6. ActualCommandHandler,即 CAccountService,将公开各种 CommandHandlerRoutines,每个都应该有一个唯一的 ID,称为 CommandID。为了处理 CommandID 管理,在充当命名空间的 CAccountService::COMMAND 的 public 部分创建一个枚举 COMMANDLIST。此枚举将包含 CAccountService 实现的 CommandHandlerRoutines 的 CommandIDs。

    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ enumeration for holding CommandIDs ------// 
            enum COMMANDLIST
            {
                Register,              
                TOTALCOMMANDS  //----1
            };
            //------ enumeration for holding CommandIDs ------// 
    
    
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    现在,客户端可以通过在他们发送给 CAccountService 的命令中指定 CommandID CAccountService::COMMAND::COMMANDLIST::Register 来访问 Register CommandHandlerRoutine。

  7. 定义 CommandHandlerRoutine。
  8. 现在定义在 CAccountService 中声明的 CommandHandlerRoutine Register

    //AccountService.cpp file
    #include "AccountService.h"
    
    //------ Defining the CommandhandlerRoutine -------//
    int CAccountService::Register(CXData *pData  /*CAccountService::COMMAND::DATA_USERINFO*/)
    {
        CAccountService::COMMAND::DATA_USERINFO *pUserInfo = 
                      (CAccountService::COMMAND::DATA_USERINFO *) pData;
    
        if(m_UserDBase.find(pUserInfo->iUserID) != m_UserDBase.end())
            return 2; //already registered.
    
        m_UserDBase[pUserInfo->iUserID] = pUserInfo->tcszUserName;
            return 1; //newly registered.
    }
    //------ Defining the CommandhandlerRoutine -------//

    由于 Register CommandHandlerRoutine 在 CAccountService::COMMAND::DATA_USERINFO 上操作,它将作为参数接收,我们可以安全地进行类型转换。现有的 CXCommandHandler 类结构确保将正确的数据传递给 CommandHandlerRoutine。在类型转换后,可以通过 pUserInfo 访问用户信息。

  9. 声明 ActualCommandHandler 所需的原生数据。
  10. Register CommandHandlerRoutine 的定义中,CAccoountService 使用 std::map m_UserDBase 来存储用户信息。ActualCommandHandler 所需的此类原生数据或数据也可以保留在 ActualCommandHandler 中。

    //AccountService.h file
    #include "CommandHandler.h"
    #include <map>
    #include <string>
    
    using namespace std;
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ enumeration for holding CommandIDs ------// 
            enum COMMANDLIST
            {
                Register,              
                TOTALCOMMANDS  //----1
            };
            //------ enumeration for holding CommandIDs ------// 
    
    
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
        
    private :
        //------- native data for CAccountService -------//
        //m_UserDBase : map[UserID] = Username
        //to store information of registered user.
        map <int, string>  m_UserDBase;
        //------- native data for CAccountService -------//
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    由于 Register CommandHandlerRoutine 在 CAccountService::COMMAND::DATA_USERINFO 上操作,它将作为参数接收,我们可以安全地进行类型转换。在类型转换后,可以通过 pUserInfo 访问用户信息。

  11. 在 CommandHandler 中注册 CommandHandlerRoutine。
  12. 到目前为止,ActualCommandHandler,即 CAccountService,通过声明 CommandID 及其操作数据等必要项,提供了 Register 服务或 CommandHandlerRoutine 的功能。现在,需要将此 CommandHandlerRoutine 注册到 ActualCommandHandler 的数据库中,该数据库存储了 ActualCommandHandler 可以响应的所有命令的信息。为了注册 ActualCommandHandler 实现的 CommandHandlerRoutine,调用基类 CXCommandHandler 已实现的 RegisterCommand 成员函数。RegisterCommand 成员函数可以在 ActualCommandHandler 的构造函数中调用。

    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ enumeration for holding CommandIDs ------// 
            enum COMMANDLIST
            {
                Register,              
                TOTALCOMMANDS  //----1
            };
            //------ enumeration for holding CommandIDs ------// 
    
    
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
    
    private :
        //------- native data for CAccountService -------//
        //m_UserDBase : map[UserID] = Username
        //to store information of registered user.
        map <int, string>  m_UserDBase;
        //------- native data for CAccountService -------//
        
    public:
        //-------- Registering the CommandHandlerRoutine -------//
        CAccountService()
        {
            RegisterCommand(CAccountService::COMMAND::COMMANDLIST::Register, 
                            &CAccountService::Register);
        }
        //-------- Registering the CommandHandlerRoutine -------//
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    注意:处理/实现命令的 CommandHandlerRoutine 应该在客户端可以发送该特定命令之前注册。

这样,我们可以创建其他的 CommandHandlerRoutines,例如:

  • Unregister
  • 电子邮件
  • 打印

请参考 AccountService.hAccountService.cpp 文件以了解这些 CommandHandlerRoutines 的实现。

8. 客户端如何向 ActualCommandHandler 发送命令

命令只不过是 CXCommand 类的一个实例。这个实例由两部分组成:

  1. CommandInfo:有关这部分的信息,请参考 CommandHandler.h 文件中的 struct CXCommand::COMMANDINFO
  2. CommandData:这是在 CActualCommandHandler::COMMAND 类中声明的数据。请参考 5. 整体类结构设计6.6.ii 声明 CommandHandlerRoutine 所需的数据

客户端创建、填充和发送命令。请参考 3. 概念解释。例如,为了 'Register' 一个用户,让我们向 ActualCommandHandler(即 CAccountService)发送一个命令。为此,创建一个 ActualCommandHandler 对象。

CAccountService oService;

客户端可以通过两种方式之一发送命令:

  1. SendCommand:使用 SendCommand 时,客户端等待命令执行完成。因此,大多数情况下,可以在栈上分配内存。
    1. 创建命令对象
    2. //command object creation
      CXCommand cmd;
    3. 创建将在命令对象中设置的命令信息对象。另外,使用 CXCommand::GetCommandInfo 成员函数对其进行初始化。
    4. //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
    5. 设置 cmdinfo 对象成员
    6. //command object creation
      CXCommand cmd;
      
      //member initialization
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      
      /**
      If the command object is created on stack, then there is no need 
      to take extra care of deallocation of memory related to command object. 
      Therefore, inform the ActualCommandHandler that it should not delete 
      the command object after responding to the command.
      For this situation use 
      CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION.
      /**/
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
    7. cmdinfo 对象设置到命令对象 cmd 中。
    8. //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
    9. 创建 CommandHandlerRoutine 将要操作的相关命令数据。同时,为这些数据赋值。
    10. int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);

      可以看出,Register CommandHandlerRoutine 期望一个 struct CAccountService::COMMAND::DATA_USERINFO 的实例。

      //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
      
      //create and initilization of related command data
      CAccountService::COMMAND::DATA_USERINFO cmddata;
      _tcscpy(cmdddata.tcszUserName, _T("schneider")); 
      cmddata.iUserID = 10;
    11. 将命令数据设置到 Command 对象中。
    12. //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
      
      //create and initilization of related command data
      CAccountService::COMMAND::DATA_USERINFO cmddata;
      _tcscpy(cmdddata.tcszUserName, _T("schneider")); 
      cmddata.iUserID = 10;
      
      //setting the related command data
      cmd.SetCommandData(&cmddata);
    13. 将命令发送到 ActualCommandHandler。
    14. //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
      
      //create and initilization of related command data
      CAccountService::COMMAND::DATA_USERINFO cmddata;
      _tcscpy(cmdddata.tcszUserName, _T("schneider")); 
      cmddata.iUserID = 10;
      
      //setting the related command data
      cmd.SetCommandData(&cmddata);
      
      //fire the command
      oService.SendCommand(&cmd);
  2. PostCommand:此方法与 SendCommand 类似,唯一的区别是客户端不等待已发送命令的执行完成。在 PostCommand 中,命令首先存储在适当的命令优先级队列中,然后从队列中获取并执行一个命令。同时,客户端代码也在并行执行。因此,如果考虑到这一点,在将命令发布到 ActualCommandHandler 之后,命令及其相关数据应该保持有效,即存储命令及其数据的内存应该保持有效/可访问。因此,在使用 PostCommand 时,客户端应该在堆上分配 Command 对象及其所有相关数据,以便它们在命令执行期间保持有效。但是等等。在 PostCommand 中,如果客户端不等待命令执行完成,那么谁将释放为 Command 对象及其相关数据分配的内存?在这种情况下,客户端可以将其内存释放的责任交给 ActualCommandHandler。
  3. //create the command object on heap
    CXCommand *pcmd = new CXCommand;
    
    //command info object creation
    CXCommand::COMMANDINFO cmdinfo;
    pcmd->GetCommandInfo(cmdinfo);
    
    
    //member initialization
    cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
    cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
    cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
    
    /**
    Since command object is created on heap therefore giving 
    the responsibility of deallocating the memory to ActualCommandHandler.
    For this situation use CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTIO.
    /**/
    cmdinfo.enumExtraInfo = CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTION;
    
    _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
    
    
    //initialize command object with necessary execution related information
    pcmd->SetCommandInfo(cmdinfo);
    
    //creating and initilization of related command data on heap
    CAccountService::COMMAND::DATA_USERINFO *pcmddata = 
                     new CAccountService::COMMAND::DATA_USERINFO;
    CXData::DATAINFO stDataInfo;
    pcmddata->GetDataInfo(stDataInfo);
    /**
    Since command data is created on heap therefore giving 
    the responsibility of deallocating the memory to ActualCommandHandler.
    For this situation use CXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE.
    /**/
    stDataInfo.enumOperationOnData = CXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE;
    pcmddata->SetDataInfo(stDataInfo);
    
    //initializing members of command data
    _tcscpy(pcmdddata->tcszUserName, _T("schneider")); 
    pcmddata->iUserID = 10;
    
    //setting the related command data
    pcmd->SetCommandData(pcmddata);
    
    //fire the command
    oService.PostCommand(pcmd);

注意

  1. 一旦通过 CXCommand::SetCommandInfo 成员函数为 CXCommand::COMMANDINFO::enumExtraInfo 指定了值 CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTION,那么不应该在发送命令(无论是通过 SendCommand 还是 PostCommand)之后再使用 Command 对象。一旦通过 CXData::SetDataInfo 成员函数为 CXData::DATAINFO::enumOperationOnData 指定了值 CXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE,那么不应该在发送命令(无论是通过 SendCommand 还是 PostCommand)之后再使用命令数据。
  2. 退出时,即当 ActualCommandHandler 即将终止时,首先执行不同优先级队列中的所有命令(如果有),然后 ActualCommandHandler 退出。

历史

  • 版本 1.0
    • 文章的初始版本。
© . All rights reserved.