Προγραμματισμός

Επεξεργασία ορισμάτων γραμμής εντολών σε Java: Η υπόθεση έκλεισε

Πολλές εφαρμογές Java που ξεκίνησαν από τη γραμμή εντολών λαμβάνουν επιχειρήματα για τον έλεγχο της συμπεριφοράς τους. Αυτά τα ορίσματα είναι διαθέσιμα στο όρισμα συστοιχίας συμβολοσειράς που μεταφέρεται στο στατικό της εφαρμογής κύριος() μέθοδος. Συνήθως, υπάρχουν δύο τύποι ορισμάτων: επιλογές (ή διακόπτες) και πραγματικά επιχειρήματα δεδομένων. Μια εφαρμογή Java πρέπει να επεξεργαστεί αυτά τα επιχειρήματα και να εκτελέσει δύο βασικές εργασίες:

  1. Ελέγξτε εάν η σύνταξη που χρησιμοποιείται είναι έγκυρη και υποστηρίζεται
  2. Ανακτήστε τα πραγματικά δεδομένα που απαιτούνται για την εκτέλεση της λειτουργίας της εφαρμογής

Συχνά, ο κώδικας που εκτελεί αυτές τις εργασίες είναι προσαρμοσμένος για κάθε εφαρμογή και συνεπώς απαιτεί σημαντική προσπάθεια τόσο για τη δημιουργία όσο και για τη συντήρηση, ειδικά εάν οι απαιτήσεις υπερβαίνουν τις απλές περιπτώσεις με μόνο μία ή δύο επιλογές. ο Επιλογές Η τάξη που περιγράφεται σε αυτό το άρθρο εφαρμόζει μια γενική προσέγγιση για τον εύκολο χειρισμό των πιο περίπλοκων καταστάσεων. Η τάξη επιτρέπει έναν απλό ορισμό των απαιτούμενων επιλογών και ορίσματα δεδομένων, και παρέχει διεξοδικούς ελέγχους σύνταξης και εύκολη πρόσβαση στα αποτελέσματα αυτών των ελέγχων. Νέες δυνατότητες Java 5, όπως γενικά και αθόρυβοι τύποι, χρησιμοποιήθηκαν επίσης για αυτό το έργο.

Τύποι ορίσματος γραμμής εντολών

Με τα χρόνια, έχω γράψει αρκετά εργαλεία Java που λαμβάνουν επιχειρήματα γραμμής εντολών για τον έλεγχο της συμπεριφοράς τους. Νωρίς, το βρήκα ενοχλητικό να δημιουργώ και να διατηρώ χειροκίνητα τον κώδικα για την επεξεργασία των διαφόρων επιλογών. Αυτό οδήγησε στην ανάπτυξη μιας πρωτότυπης κλάσης για τη διευκόλυνση αυτής της εργασίας, αλλά αυτή η τάξη είχε ομολογουμένως τους περιορισμούς της, καθώς, σε στενή επιθεώρηση, ο αριθμός πιθανών διαφορετικών ποικιλιών για επιχειρήματα γραμμής εντολών αποδείχθηκε σημαντικός. Τελικά, αποφάσισα να αναπτύξω μια γενική λύση σε αυτό το πρόβλημα.

Κατά την ανάπτυξη αυτής της λύσης, έπρεπε να λύσω δύο βασικά προβλήματα:

  1. Προσδιορίστε όλες τις ποικιλίες στις οποίες μπορούν να προκύψουν επιλογές γραμμής εντολών
  2. Βρείτε έναν απλό τρόπο για να επιτρέψετε στους χρήστες να εκφράσουν αυτές τις ποικιλίες όταν χρησιμοποιούν την τάξη που δεν έχει ακόμη αναπτυχθεί

Η ανάλυση του προβλήματος 1 οδήγησε στις ακόλουθες παρατηρήσεις:

  • Επιλογές γραμμής εντολών αντίθετες με τα ορίσματα δεδομένων γραμμής εντολών — ξεκινήστε με ένα πρόθεμα που τα αναγνωρίζει με μοναδικό τρόπο. Τα παραδείγματα προθέματος περιλαμβάνουν μια παύλα (-) σε πλατφόρμες Unix για επιλογές όπως -ένα ή κάθετο (/) σε πλατφόρμες Windows.
  • Οι επιλογές μπορεί να είναι απλοί διακόπτες (δηλαδή, -ένα μπορεί να είναι παρόν ή όχι) ή να πάρει μια τιμή. Ένα παράδειγμα είναι:

    java MyTool -a -b logfile.inp 
  • Οι επιλογές που λαμβάνουν μια τιμή μπορεί να έχουν διαφορετικούς διαχωριστές μεταξύ του πραγματικού κλειδιού επιλογής και της τιμής. Τέτοιοι διαχωριστές μπορεί να είναι ένας κενός χώρος, ένα άνω και κάτω τελεία (:) ή ένα σύμβολο ίσο (=):

    java MyTool -a -b logfile.inp java MyTool -a -b: logfile.inp java MyTool -a -b = logfile.inp 
  • Οι επιλογές που παίρνουν μια τιμή μπορούν να προσθέσουν ένα ακόμη επίπεδο πολυπλοκότητας. Σκεφτείτε τον τρόπο με τον οποίο η Java υποστηρίζει τον ορισμό των ιδιοτήτων περιβάλλοντος ως παράδειγμα:

    java -Djava.library.path = / usr / lib ... 
  • Έτσι, πέρα ​​από το πραγματικό κλειδί επιλογής (ρε), ο διαχωριστής (=), και την πραγματική τιμή της επιλογής (/ usr / lib), μια πρόσθετη παράμετρος (java.library.path) μπορεί να λάβει οποιοδήποτε αριθμό τιμών (στο παραπάνω παράδειγμα, πολλές ιδιότητες περιβάλλοντος μπορούν να καθοριστούν χρησιμοποιώντας αυτήν τη σύνταξη). Σε αυτό το άρθρο, αυτή η παράμετρος ονομάζεται "λεπτομέρεια".
  • Οι επιλογές έχουν επίσης μια ιδιότητα πολλαπλότητας: μπορεί να απαιτούνται ή προαιρετικά και ο αριθμός των επιτρεπόμενων χρόνων μπορεί επίσης να ποικίλει (όπως ακριβώς μία φορά, μία ή περισσότερες φορές ή άλλες δυνατότητες).
  • Τα ορίσματα δεδομένων είναι όλα ορίσματα γραμμής εντολών που δεν ξεκινούν με πρόθεμα. Εδώ, ο αποδεκτός αριθμός τέτοιων ορισμάτων δεδομένων μπορεί να κυμαίνεται μεταξύ ενός ελάχιστου και ενός μέγιστου αριθμού (που δεν είναι απαραίτητα το ίδιο). Επιπλέον, συνήθως μια εφαρμογή απαιτεί αυτά τα ορίσματα δεδομένων να είναι τελευταία στη γραμμή εντολών, αλλά αυτό δεν πρέπει πάντα να ισχύει. Για παράδειγμα:

    java MyTool -a -b = logfile.inp data1 data2 data3 // Όλα τα δεδομένα στο τέλος 

    ή

    java MyTool -a data1 data2 -b = logfile.inp data3 // Ίσως είναι αποδεκτό από μια εφαρμογή 
  • Οι πιο σύνθετες εφαρμογές μπορούν να υποστηρίξουν περισσότερες από μία ομάδες επιλογών:

    java MyTool -a -b datafile.inp java MyTool -k [-verbose] foo bar duh java MyTool-check-verify logfile.out 
  • Τέλος, μια εφαρμογή ενδέχεται να επιλέξει να αγνοήσει τυχόν άγνωστες επιλογές ή να θεωρήσει αυτές τις επιλογές ως σφάλμα.

Έτσι, κατά την επινόηση ενός τρόπου να επιτρέπεται στους χρήστες να εκφράζουν όλες αυτές τις ποικιλίες, βρήκα την ακόλουθη γενική φόρμα επιλογών, η οποία χρησιμοποιείται ως βάση για αυτό το άρθρο:

[[]] 

Αυτή η φόρμα πρέπει να συνδυαστεί με την ιδιότητα πολλαπλότητας όπως περιγράφεται παραπάνω.

Εντός των περιορισμών της γενικής μορφής μιας επιλογής που περιγράφεται παραπάνω, το Επιλογές κλάση που περιγράφεται σε αυτό το άρθρο έχει σχεδιαστεί για να είναι η γενική λύση για οποιεσδήποτε ανάγκες επεξεργασίας γραμμής εντολών που μπορεί να έχει μια εφαρμογή Java.

Τα μαθήματα βοηθού

ο Επιλογές class, η οποία είναι η βασική κατηγορία για τη λύση που περιγράφεται σε αυτό το άρθρο, συνοδεύεται από δύο βοηθητικές τάξεις:

  1. OptionData: Αυτή η τάξη περιέχει όλες τις πληροφορίες για μία συγκεκριμένη επιλογή
  2. OptionSet: Αυτή η τάξη διαθέτει ένα σύνολο επιλογών. Επιλογές το ίδιο μπορεί να κρατήσει οποιονδήποτε αριθμό τέτοιων συνόλων

Πριν περιγράψετε τις λεπτομέρειες αυτών των τάξεων, άλλες σημαντικές έννοιες του Επιλογές πρέπει να εισαχθεί τάξη.

Typesafe αθροίσματα

Το πρόθεμα, το διαχωριστικό και η ιδιότητα πολλαπλότητας καταγράφηκαν από enums, μια δυνατότητα που παρέχεται για πρώτη φορά από το Java 5:

δημόσιο πρόθεμα enum {DASH ('-'), SLASH ('/'); ιδιωτικός χαρακτήρας c; ιδιωτικό πρόθεμα (char c) {this.c = c; } char getName () {return c; }} δημόσιο διαχωριστικό enum {COLON (':'), EQUALS ('='), BLANK (''), NONE ('D'); ιδιωτικός χαρακτήρας c; ιδιωτικό διαχωριστικό (char c) {this.c = c; } char getName () {return c; }} δημόσιο enum πολλαπλότητα {ONCE, ONCE_OR_MORE, ZERO_OR_ONE, ZERO_OR_MORE; } 

Η χρήση enums έχει ορισμένα πλεονεκτήματα: αυξημένη ασφάλεια τύπου και στενός, εύκολος έλεγχος του συνόλου των επιτρεπόμενων τιμών. Τα αθροίσματα μπορούν επίσης να χρησιμοποιηθούν εύκολα με γενικές συλλογές.

Σημειώστε ότι το Πρόθεμα και Διαχωριστής Οι αριθμοί έχουν τους δικούς τους κατασκευαστές, επιτρέποντας τον ορισμό ενός πραγματικού χαρακτήρας που αντιπροσωπεύει αυτήν την παρουσία enum (έναντι του όνομα χρησιμοποιείται για να αναφέρεται στη συγκεκριμένη περίπτωση enum). Αυτοί οι χαρακτήρες μπορούν να ανακτηθούν χρησιμοποιώντας αυτά τα ποσά getName () μεθόδους και οι χαρακτήρες χρησιμοποιούνται για το java.util.regex σύνταξη μοτίβου πακέτου. Αυτό το πακέτο χρησιμοποιείται για την εκτέλεση ορισμένων ελέγχων σύνταξης στο Επιλογές τάξη, λεπτομέρειες της οποίας θα ακολουθήσουν.

ο Πολλαπλότητα Το enum υποστηρίζει επί του παρόντος τέσσερις διαφορετικές τιμές:

  1. ΜΙΑ ΦΟΡΑ: Η επιλογή πρέπει να πραγματοποιηθεί ακριβώς μία φορά
  2. ONCE_OR_MORE: Η επιλογή πρέπει να πραγματοποιηθεί τουλάχιστον μία φορά
  3. ZERO_OR_ONCE: Η επιλογή μπορεί να απουσιάζει ή να εμφανίζεται ακριβώς μία φορά
  4. ZERO_OR_MORE: Η επιλογή μπορεί να απουσιάζει ή να εμφανίζεται πολλές φορές

Περισσότεροι ορισμοί μπορούν εύκολα να προστεθούν σε περίπτωση ανάγκης.

Η τάξη OptionData

ο OptionData Η κλάση είναι βασικά ένα κοντέινερ δεδομένων: πρώτον, για τα δεδομένα που περιγράφουν την ίδια την επιλογή, και δεύτερον, για τα πραγματικά δεδομένα που βρίσκονται στη γραμμή εντολών για αυτήν την επιλογή. Αυτός ο σχεδιασμός αντικατοπτρίζεται ήδη στον κατασκευαστή:

OptionData (Επιλογές. Πρόθεμα προθέματος, πλήκτρο συμβολοσειράς, boolean λεπτομέρεια, Επιλογές. Διαχωριστικό διαχωριστή, τιμή boolean, Επιλογές. Πολλαπλότητα πολλαπλότητας) 

Το κλειδί χρησιμοποιείται ως το μοναδικό αναγνωριστικό για αυτήν την επιλογή. Σημειώστε ότι αυτά τα επιχειρήματα αντικατοπτρίζουν άμεσα τα ευρήματα που περιγράφηκαν νωρίτερα: μια πλήρης περιγραφή επιλογής πρέπει να έχει τουλάχιστον πρόθεμα, κλειδί και πολλαπλότητα. Οι επιλογές που λαμβάνουν μια τιμή έχουν επίσης διαχωριστικό και ενδέχεται να αποδεχτούν λεπτομέρειες. Σημειώστε επίσης ότι αυτός ο κατασκευαστής έχει πρόσβαση σε πακέτα, επομένως οι εφαρμογές δεν μπορούν να το χρησιμοποιήσουν απευθείας. Τάξη OptionSet'μικρό addOption () μέθοδος προσθέτει τις επιλογές. Αυτή η αρχή σχεδιασμού έχει το πλεονέκτημα ότι έχουμε πολύ καλύτερο έλεγχο στους πραγματικούς πιθανούς συνδυασμούς επιχειρημάτων που χρησιμοποιήθηκαν για τη δημιουργία OptionData περιπτώσεις. Για παράδειγμα, εάν αυτός ο κατασκευαστής ήταν δημόσιος, θα μπορούσατε να δημιουργήσετε μια παρουσία με λεπτομέρειες που έχουν οριστεί σε αληθής και η τιμή ορίστηκε σε ψευδής, που είναι φυσικά ανοησία. Αντί να έχω πολύπλοκους ελέγχους στον ίδιο τον κατασκευαστή, αποφάσισα να παράσχω ένα ελεγχόμενο σύνολο addOption () μεθόδους.

Ο κατασκευαστής δημιουργεί επίσης μια παρουσία του java.util.regex.Pattern, που χρησιμοποιείται για τη διαδικασία αντιστοίχισης προτύπων αυτής της επιλογής. Ένα παράδειγμα θα ήταν το μοτίβο για μια επιλογή που παίρνει μια τιμή, χωρίς λεπτομέρειες και ένα μη κενό διαχωριστικό:

pattern = java.util.regex.Pattern.compile (prefix.getName () + key + separator.getName () + "(. +) $"); 

ο OptionData τάξη, όπως ήδη αναφέρθηκε, διατηρεί επίσης τα αποτελέσματα των ελέγχων που διενεργούνται από το Επιλογές τάξη. Παρέχει τις ακόλουθες δημόσιες μεθόδους για πρόσβαση σε αυτά τα αποτελέσματα:

int getResultCount () String getResultValue (int index) String getResultDetail (int index) 

Η πρώτη μέθοδος, getResultCount (), επιστρέφει τον αριθμό των φορών που βρέθηκε μια επιλογή. Αυτός ο σχεδιασμός μεθόδου συνδέεται άμεσα με την πολλαπλότητα που ορίζεται για την επιλογή. Για επιλογές που λαμβάνουν μια τιμή, αυτή η τιμή μπορεί να ανακτηθεί χρησιμοποιώντας το getResultValue (ευρετήριο int) μέθοδο, όπου ο δείκτης μπορεί να κυμαίνεται μεταξύ 0 και getResultCount () - 1. Για επιλογές αξίας που δέχονται επίσης λεπτομέρειες, αυτές μπορούν να έχουν παρόμοια πρόσβαση χρησιμοποιώντας το getResultDetail (int ευρετήριο) μέθοδος.

Η τάξη OptionSet

ο OptionSet Το class είναι βασικά ένα δοχείο για ένα σύνολο OptionData παρουσίες και επίσης τα ορίσματα δεδομένων που βρίσκονται στη γραμμή εντολών.

Ο κατασκευαστής έχει τη μορφή:

OptionSet (Options.Prefix prefix, Options.Multiplicity defaultMultiplicity, String setName, int minData, int maxData) 

Και πάλι, αυτός ο κατασκευαστής έχει πρόσβαση στο πακέτο. Τα σύνολα επιλογών μπορούν να δημιουργηθούν μόνο μέσω του Επιλογές η τάξη είναι διαφορετική addSet () μεθόδους. Η προεπιλεγμένη πολλαπλότητα για τις επιλογές που καθορίζονται εδώ μπορεί να παρακαμφθεί κατά την προσθήκη μιας επιλογής στο σύνολο. Το όνομα του συνόλου που καθορίζεται εδώ είναι ένα μοναδικό αναγνωριστικό που αναφέρεται στο σετ. ελάχιστα δεδομένα και μέγιστα δεδομένα είναι ο ελάχιστος και μέγιστος αριθμός αποδεκτών ορισμάτων δεδομένων για αυτό το σύνολο.

Το δημόσιο API για OptionSet περιέχει τις ακόλουθες μεθόδους:

Γενικές μέθοδοι πρόσβασης:

String getSetName () int getMinData () int getMaxData () 

Μέθοδοι προσθήκης επιλογών:

OptionSet addOption (String key) OptionSet addOption (String key, Multiplicity multiplicity) OptionSet addOption (String key, Separator separator) OptionSet addOption (String key, Separator separator, Multiplicity multiplicity) OptionSet addOption (Κλειδί συμβολοσειράς, boolean λεπτομέρειες, Διαχωριστής επιλογών) (Πλήκτρο συμβολοσειράς, boolean λεπτομέρειες, διαχωριστικό διαχωριστή, πολλαπλότητα πολλαπλότητας) 

Μέθοδοι πρόσβασης στα δεδομένα αποτελεσμάτων ελέγχου:

java.util.ArrayList getOptionData () OptionData getOption (String key) boolean isSet (String key) java.util.ArrayList getData () java.util.ArrayList getUnmatched () 

Σημειώστε ότι οι μέθοδοι για την προσθήκη επιλογών που χρειάζονται ένα Διαχωριστής επιχείρημα δημιουργήστε ένα OptionData παράδειγμα αποδοχή μιας τιμής. ο addOption () Οι μέθοδοι επιστρέφουν το ίδιο το στιγμιότυπο, το οποίο επιτρέπει την αλυσίδα επίκλησης:

Επιλογές επιλογών = νέες επιλογές (args); options.addSet ("MySet"). addOption ("a"). addOption ("b"); 

Μετά την πραγματοποίηση των ελέγχων, τα αποτελέσματά τους είναι διαθέσιμα με τις υπόλοιπες μεθόδους. getOptionData () επιστρέφει μια λίστα όλων OptionData περιπτώσεις, ενώ getOption () επιτρέπει άμεση πρόσβαση σε μια συγκεκριμένη επιλογή. isSet (πλήκτρο συμβολοσειράς) είναι μια μέθοδος ευκολίας που ελέγχει αν βρέθηκαν επιλογές τουλάχιστον μία φορά στη γραμμή εντολών. getData () παρέχει πρόσβαση στα επιχειρήματα δεδομένων που βρέθηκαν, ενώ getUnmatched () παραθέτει όλες τις επιλογές που βρίσκονται στη γραμμή εντολών για τις οποίες δεν ταιριάζει OptionData βρέθηκαν περιπτώσεις.

Η κατηγορία Επιλογές

Επιλογές είναι η βασική τάξη με την οποία αλληλεπιδρούν οι εφαρμογές. Παρέχει πολλούς κατασκευαστές, οι οποίοι λαμβάνουν όλες τις συστοιχίες συμβολοσειρών ορίσματος γραμμής εντολών που κύριος() Η μέθοδος παρέχει ως το πρώτο επιχείρημα:

Επιλογές (String args []) Επιλογές (String args [], int data) Options (String args [], int defMinData, int defMaxData) Επιλογές (String args [], Multiplicity defaultMultiplicity) Επιλογές int πρόθεμα, int defMinData, int defMaxData) Επιλογές (String args [], Prefix prefix, Multiplicity defaultMultiplicity) Options (String args [], prefix prefix, Multiplicity defaultMultiplicity, int data) Options (String args [], Prefix prefix, Multiplicity defaultMultiplicity, int defMinData, int defMaxData) 

Ο πρώτος κατασκευαστής σε αυτήν τη λίστα είναι ο απλούστερος χρησιμοποιώντας όλες τις προεπιλεγμένες τιμές, ενώ η τελευταία είναι η πιο γενική.

Πίνακας 1: Επιχειρήματα για τους κατασκευαστές Επιλογών () και τη σημασία τους

αξία Περιγραφή Προκαθορισμένο
πρόθεμαΑυτό το όρισμα κατασκευαστή είναι το μόνο μέρος όπου μπορεί να καθοριστεί ένα πρόθεμα. Αυτή η τιμή μεταβιβάζεται σε οποιοδήποτε σύνολο επιλογών και οποιαδήποτε επιλογή δημιουργήθηκε στη συνέχεια. Η ιδέα πίσω από αυτήν την προσέγγιση είναι ότι μέσα σε μια δεδομένη εφαρμογή, αποδεικνύεται απίθανο να χρειαστούν διαφορετικά προθέματα.Πρόθεμα.DASH
προεπιλεγμένη πολλαπλότηταΑυτή η προεπιλεγμένη πολλαπλότητα μεταβιβάζεται σε κάθε σύνολο επιλογών και χρησιμοποιείται ως προεπιλογή για επιλογές που προστίθενται σε ένα σύνολο χωρίς να καθορίζεται πολλαπλότητα. Φυσικά, αυτή η πολλαπλότητα μπορεί να παρακαμφθεί για κάθε προστιθέμενη επιλογή.Πολλαπλότητα. ΜΙΑ
defMinDatadefMinData είναι ο προεπιλεγμένος ελάχιστος αριθμός υποστηριζόμενων ορισμάτων δεδομένων που μεταβιβάζονται σε κάθε σύνολο επιλογών, αλλά μπορεί φυσικά να παρακαμφθεί κατά την προσθήκη ενός συνόλου.0
defMaxDatadefMaxData είναι ο προεπιλεγμένος μέγιστος αριθμός υποστηριζόμενων ορισμάτων δεδομένων που μεταβιβάζονται σε κάθε σύνολο επιλογών, αλλά μπορεί φυσικά να παρακαμφθεί κατά την προσθήκη ενός συνόλου.0