8

I'm writing a debugger for a Z80-emulator we are writing in a school project, using Java. The debugger reads a command from the user, executes it, reads another command, etc.

Commands can either be argument less, have optional arguments, or take an unlimited amount of arguments. Arguments are mostly integers, but occasionally they're strings.

Currently, we're using the Scanner-class for reading and parsing input. 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).

This was a kludge written in the beginning of the project, which quickly got out of hand as we added more and more commands to the debugger.

The major issues I have with this code is the large amount of repetition, the highlevel of if/else-nestedness, and the all around uglyness.

I would like suggestions on how to make this code more beautiful and modular, and what kind of patterns that are suitable for this kind of program.

I would also like more general suggestions on code style.

4

2 に答える 2

2

うん、特に Java やその他の OO 言語では、より簡単で優れた方法があります。

まず、基本的な洞察は、コマンド パーサーが有限状態マシンであるということです。START 状態は空の行 (または行の先頭のインデックス) です。

考えてみましょうecho

$ echo foo bat "bletch quux"
  1. 行を分割してトークン化します。

    "echo" "foo" "bar" "bletch quux"

  2. シェルでは、文法は通常動詞名詞名詞名詞です...そのように解釈してください。if-else if のシーケンスでそれを行うことができますが、ハッシュの方が優れています。文字列をインデックスとしてハッシュをロードし、別のものにインデックスを付けます。これは、スイッチに入る単なる数字である可能性があります。

(これは疑似コードです):

  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 および Object Factory パターンを適用できます。これでクラスコマンドができました

  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次に、メソッドを使用してそのコマンドの処理を呼び出します。

アップデート

Factory パターンを使用するという点で、これは少し一般的すぎるかもしれません。なぜファクトリーメソッドがあるのですか?主に、コマンドを実行するたびに、「動詞」オブジェクト (のインスタンスなどEcho) が独自の内部状態を持つことができるように、これを使用します。状態を長期間維持する必要がない場合は、これを単純化して次のようにできます。

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

Echoでインスタンス化したときに作成したオブジェクトを取得して使用するだけですcmds.add("echo", new Echo());

于 2009-04-04T19:17:36.807 に答える
1

マップでディスパッチを行うことを検討しましたか? ハッシュマップをそこに入れるのはとても簡単です。キーをコマンドにして、次のようなコマンドであるインターフェイスまたは抽象クラスを作成します。

interface Commmand {
   void execute(String args);
}

または、事前に引数を切り刻むこともできます。

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

次に、HashMap<String,Command> を使用します。

于 2009-04-04T19:12:11.000 に答える