Macchina a stati della sequenza

Aug 23 2020

Ho provato ad analizzare gli argomenti della riga di comando. Il programma richiede quattro argomenti. Ripeto gli argomenti. Se l'argomento è un'opzione, elaboro l'opzione. Altrimenti l'argomento è uno degli argomenti obbligatori. Per leggere gli argomenti richiesti ho bisogno di una sorta di macchina a stati. Nel primo caso dovrebbe essere letto il primo argomento. Nel secondo caso il secondo argomento e così via.

Ho scritto una Procclasse con un solo metodo, che restituisce nuovamente una Procclasse.

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

In questo modo posso eseguire alcune azioni e definire cosa dovrebbe essere fatto dopo.

  1. salva host db e poi leggi nome
  2. salvare il nome del database e quindi leggere l'utente
  3. salva utente db e poi leggi il file xml
  4. salva il file xml e poi niente

Ma a causa di tutto il sovraccarico della classe è difficile da leggere.

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;
                }
              };
            }
          };
        }
      };
    }
  };

C'è un modo per semplificare il codice? Ho provato Lambdas, ma mi sembra che Lambdas possa usare solo variabili finali, il che è un po 'inutile, quando voglio memorizzare un valore.

Esempio completo:

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);
  }
}

Risposte

5 Bobby Aug 23 2020 at 23:46

Innanzitutto, dovresti seguire le convenzioni di denominazione Java .


Secondo, non abbreviare i nomi solo perché puoi. Esempio, cosa significa "Proc"? Procedura? Processore? Anche se i nomi diventano più lunghi, la leggibilità ne vale la pena!


static String db_host = null;

I tuoi membri non dovrebbero essere static, e dovrebbero idealmente essere qualificati con qualcos'altro che package-private, come protectedo privateper mostrare chiaramente l'intento.


Per leggere gli argomenti richiesti ho bisogno di una sorta di macchina a stati.

È un requisito che ti è stato dato o è un'ipotesi che avevi? Perché non è affatto vero. Nel complesso, il tuo sistema è completamente inutilmente complesso senza una ragione o un vantaggio ovvio. Né sembra affatto una macchina a stati, è solo una catena eccessivamente complessa di chiamate di funzione.

Una macchina a stati per l'analisi degli argomenti del comando sarebbe qualcosa del genere:

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;
    }
}

E anche questo è stranamente complesso.

Quello che vuoi è la soluzione più semplice che funzioni, e sarebbe un'estrazione hardcoded di argomenti:

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];

Il che, ovviamente, non è carino, ma la soluzione più semplice con cui puoi farla franca. Naturalmente, la lunghezza dell'array dovrebbe essere controllata in anticipo, sia che si tratti di 4 elementi o più. Successivamente dovresti controllare se tutti i parametri sono impostati, in caso contrario, esci con un errore.

Se vuoi una soluzione più sofisticata, dovresti scrivere un parser completo di argomenti, un esercizio non così banale.