草庐IT

c++ - 多态命令解析器设计

coder 2024-02-15 原文

希望对我正在尝试解决的这个问题提出一些意见。我正在尝试改进我的 OO 体验并充分利用 C++ 的多态功能。我正在尝试为基本命令解析器编写一些代码。他们的命令结构如下:

[命令名称] [参数]

命令名称将仅限于一个单词字符串。参数可以是 0 到 N 的字符串列表。

每个命令和参数列表都可以指向我系统中的任何种类的软件对象。因此,例如,我可以将 rtp 统计命令映射到我的 rtp 模块,将用户统计信息映射到我的用户模块。像那样的东西。

现在,我的 CLI 的入口点将整个命令字符串作为标准字符串提供。并且它提供了一个标准的输出流来向用户显示结果。

我真的想避免使用解析器函数然后做 if then else 之类的处理。所以我在想这样的事情:

  1. 我会有一个名为 command 的基类。它的构造函数将采用字符串命令、标准输出和它需要与之交互的对象的接口(interface)。
  2. 我会创建一个命令工厂,将命令名称与处理它的对象相匹配。这将为正确的命令实例化正确的命令对象。
  3. 每个单独的命令对象都会解析给定的参数并为此命令做出正确的选择。

我正在苦苦挣扎的是如何将正确的模块提供给正确的命令。这是我应该使用模板参数的地方吗?这样每个命令都可以采用任何接口(interface),我会让工厂决定将哪个模块传递给命令对象?

我也愿意接受其他意见。我只是想学习并希望社区能给我一些提示 :-)。

最佳答案

您正在寻找的是 OOP 中的常见模式。 Design Patterns (四人帮的书)将此称为 Command Pattern .

通常不需要模板。一切都在运行时进行解析和调度,因此动态多态(虚函数)可能是更好的选择。

在另一个答案中,Rafael Baptista 提出了一个基本设计。以下是我将如何修改他的设计以使其更加完整:

命令对象和 CommandDispatcher

命令由 Command 类的子类处理。命令由 CommandDispatcher 对象分派(dispatch),该对象处理命令字符串的基本解析(基本上,按空格拆分,可能处理带引号的字符串等)。

系统向CommandDispatcher注册一个Command实例,并将每个Command实例与一个命令名(std::字符串)。关联由 std::map 对象处理,尽管可以用哈希表(或关联键值对的类似结构)代替。

class Command
{
  public:
    virtual ~Command(void);
    virtual void execute(FILE* in, const std::vector<std::string>& args) = 0;
};

class CommandDispatcher
{
  public:
    typedef std::map<std::string, Command*> CommandMap;

    void registerCommand(const std::string& commandName, Command* command)
    {
      CommandMap::const_iterator cmdPair = registeredCommands.find(commandName);
      if (cmdPair != registeredCommands.end())
      {
        // handle error: command already registered
      }
      else
      {
        registeredCommands[commandName] = command;
      }
    }

    // possibly include isRegistered, unregisterCommand, etc.

    void run(FILE* in, const std::string& unparsedCommandLine); // parse arguments, call command
    void dispatch(FILE* in, const std::vector<std::string>& args)
    {
      if (! args.empty())
      {
        CommandMap::const_iterator cmdPair = registeredCommands.find(args[0]);
        if (cmdPair == registeredCommands.end())
        {
          // handle error: command not found
        }
        else
        {
          Command* cmd = cmdPair->second;
          cmd->execute(in, args);
        }
      }
    }


  private:
    CommandMap registeredCommands;
};

我省略了解析和其他细节,但这是命令模式的一种非常常见的结构。请注意 std::map 如何处理命令名称与命令对象的关联。

注册命令

要使用此设计,您需要在系统中注册命令。您需要实例化 CommandDispatcher,或者使用 Singleton模式,在 main 中,或在另一个中心位置。

然后,您需要注册命令对象。有几种方法可以做到这一点。我更喜欢的方式是让每个模块(相关命令集)提供自己的注册功能,因为您有更多的控制权。例如,如果您有一个“文件 IO”模块,那么您可能有一个函数 fileio_register_commands:

void fileio_register_commands(CommandDispatcher* dispatcher)
{
  dispatcher->registerCommand( "readfile", new ReadFileCommand );
  dispatcher->registerCommand( "writefile", new WriteFileCommand );
  // etc.
}

此处 ReadFileCommandWriteFileCommand 是实现所需行为的 Command 的子类。

您必须确保在命令可用之前调用 fileio_register_commands

这种方法可以用于动态加载的库(DLL 或共享库)。确保注册命令的函数具有基于模块名称的规则模式:XXX_register_commands,其中 XXX 是例如小写的模块名称。加载共享库或 DLL 后,您的代码可以判断是否存在这样的函数,然后调用它。

关于c++ - 多态命令解析器设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11250710/

有关c++ - 多态命令解析器设计的更多相关文章

  1. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  5. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  6. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  7. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  8. ruby-on-rails - 我更新了 ruby​​ gems,现在到处都收到解析树错误和弃用警告! - 2

    简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und

  9. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  10. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

随机推荐