编写更清晰,更模块化的命令解析器

时间:2009-04-04 18:49:13

标签: java design-patterns java.util.scanner

我正在为一个Z80模拟器编写调试器,我们正在学校项目中使用Java编写。调试器从用户读取命令,执行它,读取另一个命令等。

命令可以是较少的参数,具有可选参数,也可以使用无限量的参数。参数主要是整数,但偶尔它们是字符串。

目前,我们正在使用Scanner-class来读取和解析输入。 The read-method looks kinda like like this (I'm writing this off the top of my head, not paying attention to syntax nor correctness).

这是在项目开始时写的一个kludge,当我们向调试器添加越来越多的命令时,它很快就失控了。

我对这段代码的主要问题是大量的重复,if / else-nestedness的高级别以及整个丑陋。

我想建议如何使这个代码更美观和模块化,以及哪种模式适合这种程序。

我还想了解更多有关代码风格的一般建议。

2 个答案:

答案 0 :(得分:2)

是的,有一种更简单/更好的方法,特别是在Java或其他OO语言中。

首先,基本的见解是你的命令解析器是一个有限状态机:START状态是一个空行(或一行开头的索引)。

让我们考虑echo

$ echo foo bat "bletch quux"
  1. 将该行标记为多个部分:

    “echo”“foo”“bar”“bletch quux”

  2. 在shell中,语法通常是动词名词名词名词...... 所以这样解释它。你可以使用if-else序列来完成它,但哈希更好。您使用字符串作为索引加载哈希,并索引其他内容。它可能只是一个数字,它将进入一个开关:

  3. (这是伪代码):

      Hashtable cmds = new Hashtable();
      enum cmdindx { ECHO=1, LS=2, ...}
      cmds.add("echo", ECHO); // ...
    
      // get the token into tok
      switch (cmds.get(tok)) {
         case ECHO: // process it
           // get each successor token and copy it to stdout
           break;
         ...
         default:
            // didn't recognize the token
            // process errors
       }
    

    更好的是,您可以应用命令和对象工厂模式。现在你有一个类Command

      public interface Command {
         public void doThis(String[] nouns) ;
         public Command factory();
      }
    
      public class Echo implements Command {
         public void doThis(String[] nouns){
             // the code is foreach noun in nouns, echo it
         }
         public Command factory(){
             // this clones the object and returns it
         }
      }
    

    现在,您的代码变为

      // Load the hash
      Hashtable cmds = new Hashtable();
      cmds.add("echo", new Echo());  // one for each command
    
    
      // token is in tok
      // the "nouns" or "arguments are in a String[] nouns
      ((cmds.get(tok)).factory()).doThis(nouns);
    

    看看这是如何工作的?您在哈希中查找对象。您调用factory方法获取新副本。然后使用doThis方法调用该命令的处理。

    更新

    这可能有点一般,因为它使用工厂模式。为什么有工厂方法?主要是,您可以使用它,以便每次执行命令时,“动词”对象(如Echo的实例)都可以拥有自己的内部状态。如果您不需要状态持续很长时间,可以将其简化为

      (cmds.get(tok)).doThis(nouns);
    

    现在它只是获取并使用您使用Echo实例化时创建的cmds.add("echo", new Echo());对象。

答案 1 :(得分:1)

您是否考虑过使用地图进行调度?一个hashmap很容易放在那里。只需将密钥作为命令,然后创建一个接口或抽象类,它就是这样的命令:

interface Commmand {
   void execute(String args);
}

或者更好的是你可以事先砍掉论据:

interface Commmand {
   void execute(String[] args);
}

然后你会使用HashMap< String,Command>。