Sequenzzustandsmaschine

Aug 23 2020

Ich habe versucht, Befehlszeilenargumente zu analysieren. Das Programm benötigt vier Argumente. Ich iteriere über die Argumente. Wenn das Argument eine Option ist, verarbeite ich die Option. Andernfalls ist das Argument eines der erforderlichen Argumente. Um die erforderlichen Argumente zu lesen, benötige ich eine Art Zustandsmaschine. Im ersten Fall sollte das erste Argument gelesen werden. Im zweiten Fall das zweite Argument und so weiter.

Ich habe eine ProcKlasse mit nur einer Methode geschrieben, die wieder eine ProcKlasse zurückgibt .

static abstract class Proc {
  abstract Proc exec (String arg);
}

Auf diese Weise kann ich eine Aktion ausführen und definieren, was als nächstes getan werden soll.

  1. Speichern Sie den DB-Host und lesen Sie den Namen
  2. Speichern Sie den Datenbanknamen und lesen Sie den Benutzer
  3. Speichern Sie den Datenbankbenutzer und lesen Sie die XML-Datei
  4. Speichern Sie die XML-Datei und dann nichts

Aber wegen des ganzen Klassenaufwands ist es schwer zu lesen.

Proc proc = new Proc () {
    Proc exec (String arg) {
      db_host = arg;
      return new Proc () {
        Proc exec (String arg) {
          db_name = arg;
          return new Proc () {
            Proc exec (String arg) {
              db_user = arg;
              return new Proc () {
                Proc exec (String arg) {
                  xml_file = arg;
                  return null;
                }
              };
            }
          };
        }
      };
    }
  };

Gibt es eine Möglichkeit, den Code zu vereinfachen? Ich habe Lambdas ausprobiert, aber es scheint mir, dass Lambdas nur endgültige Variablen verwenden kann, was ein bisschen nutzlos ist, wenn ich einen Wert speichern möchte.

Vollständiges Beispiel:

public class Import
{
  static String db_host = null;
  static String db_port = "5432";
  static String db_name = null;
  static String db_user = null;
  static String xml_file = null;

  static void usage ()
  {
    System.err.println ("Usage: Import [-p PORT] HOST DATABASE USER FILE");
  }

  static abstract class Proc {
    abstract Proc exec (String arg);
  }

  static void parse_args (String[] args)
  {
    Proc proc = new Proc () {
        Proc exec (String arg) {
          db_host = arg;
          return new Proc () {
            Proc exec (String arg) {
              db_name = arg;
              return new Proc () {
                Proc exec (String arg) {
                  db_user = arg;
                  return new Proc () {
                    Proc exec (String arg) {
                      xml_file = arg;
                      return null;
                    }
                  };
                }
              };
            }
          };
        }
      };

    try {
      for (int i = 0; i < args.length; i++)
        switch (args[i]) {
        case "-p":
          db_port = args[++i];
          break;
        case "-h":
          usage ();
          break;
        default:
          proc = proc.exec (args[i]);
        }
    }
    catch (Exception ex) {
      throw new Error ("Can not parse args!", ex);
    }
  }

  public static void main (String[] args)
  {
    parse_args (args);

    System.err.println ("db_host: " + db_host);
    System.err.println ("db_port: " + db_port);
    System.err.println ("db_name: " + db_name);
    System.err.println ("db_user: " + db_user);
    System.err.println ("xml_file: " + xml_file);
  }
}

Antworten

5 Bobby Aug 23 2020 at 23:46

Befolgen Sie zunächst die Java-Namenskonventionen .


Zweitens, kürzen Sie Namen nicht, nur weil Sie können. Beispiel, wofür steht "Proc"? Verfahren? Prozessor? Auch wenn Namen länger werden, lohnt sich die Lesbarkeit!


static String db_host = null;

Ihre Mitglieder sollten nicht sein static, und sollte idealerweise qualifiziert sein mit etwas anderes als package-private, wie protectedoder privatedie Absicht deutlich zu zeigen.


Um die erforderlichen Argumente zu lesen, benötige ich eine Art Zustandsmaschine.

Ist das eine Anforderung, die Ihnen gegeben wurde, oder ist das eine Annahme, die Sie hatten? Weil das überhaupt nicht stimmt. Insgesamt ist Ihr System ohne offensichtlichen Grund oder Nutzen völlig unnötig komplex. Es scheint auch überhaupt keine Zustandsmaschine zu sein, es ist nur eine übermäßig komplexe Kette von Funktionsaufrufen.

Eine Zustandsmaschine für das Parsen von Befehlsargumenten wäre ungefähr so:

for (int index = 0; index < args.length; index++) {
    String arg = args[index];
    
    if (arg.equals("-p") && databaseHost == null) {
        arg = args[++index];
        
        databasePort = arg;
    } else if (databaseHost == null) {
        databaseHost = arg;
    } else if (databaseUsername == null) {
        databaseUsername = arg;
    } else if (databasePassword == null) {
        databasePassword = arg;
    } else if (inputFile == null) {
        inputFile = arg;
    }
}

Und selbst das ist seltsamerweise komplex.

Was Sie wollen, ist die einfachste Lösung, die funktioniert, und das wäre eine fest codierte Extraktion von Argumenten:

if (args[0].equals("-h")) {
    printHelp();
    return;
}

int portProvidedOffset = 0;

if (args[0].equals("-p")) {
    databasePort = args[1];
    
    portProvidedOffset = 2;
}

databaseHost = args[portProvidedOffset + 0];
databaseUsername = args[portProvidedOffset + 1];
databasePassword = args[portProvidedOffset + 2];
inputFile = args[portProvidedOffset + 3];

Das ist natürlich nicht schön, aber die einfachste Lösung, mit der Sie durchkommen können. Natürlich sollte die Länge des Arrays vorher überprüft werden, ob es sich um 4 oder mehr Elemente handelt. Anschließend prüfen Sie, ob alle Parameter eingestellt sind. Wenn nicht, beenden Sie das Programm mit einem Fehler.

Wenn Sie eine ausgefeiltere Lösung wünschen, müssen Sie sich einen vollständigen Argument-Parser schreiben, eine nicht so triviale Übung.