Generisches Datei-Parser-Design in Java unter Verwendung des Strategie-Musters

14

Ich arbeite an einem Produkt, für das eines der Module die Aufgabe hat, XML-Dateien zu analysieren und den erforderlichen Inhalt in einer Datenbank abzulegen. Obwohl die gegenwärtige Anforderung nur darin besteht, XML-Dateien zu analysieren, möchte ich mein Parsing-Modul so gestalten, dass ich in Zukunft alle Arten von Dateien unterstützen kann. Der Grund für diesen Ansatz ist, dass wir dieses Produkt für einen bestimmten Kunden entwickeln, es aber in naher Zukunft an andere Kunden verkaufen möchten. Alle Systeme im Ökosystem für den aktuellen Client produzieren und konsumieren XML-Dateien. Dies ist jedoch möglicherweise bei anderen Clients nicht der Fall.

Was habe ich bisher versucht? (Die Gegenwart) Ich habe das folgende Design im Auge, das auf dem Strategiemuster basiert. Ich habe den Code in Eclipse schnell aufgeschrieben, um mein Design zu vermitteln. Daher wäre es großartig, wenn andere Aspekte wie die richtige Behandlung von Ausnahmen vorerst ignoriert würden.

Parser: Die Strategieschnittstelle, die eine Analysemethode verfügbar macht.

 public interface Parser<T> {
        public T parse(String inputFile);
    }

* Der Grund für die Verwendung eines generischen Parameters besteht darin, jeden Rückgabetyp zuzulassen und die Typensicherheit bei der Kompilierung zu gewährleisten.

ProductDataXmlParser Eine konkrete Klasse zum Parsen einer product.xml-Datei, die produktbezogene Informationen enthält. (mit XMLBeans)

public class ProductDataXmlParser implements Parser<ProductDataTYPE> {

    public ProductDataTYPE parse(String inputFile) {
        ProductDataTYPE productDataDoc = null;
            File inputXMLFile = new File(inputFile);

        try {
            productDataDoc = ProductDataDocument.Factory.parse(inputXMLFile);
        } catch(XmlException e) {
            System.out.println("XmlException while parsing file : "+inputXMLFile);
        } catch(IOException e) { 
                 System.out.println("IOException while parsing file : "+inputXMLFile);
        }
        return productDataDoc.getProductData();
    }
} 

Dabei gilt Folgendes: ProductDataTYPE und ProductDataDocument sind XMlBean-POJO-Klassen, die mit xsd und dem Befehl scomp generiert wurden.

Die Zukunft

Wenn ich eine product.txt-Datei habe, die in Zukunft analysiert werden soll, kann ich mein eigenes POJO mit dem Namen ProductData definieren, das den erforderlichen Inhalt der Datei enthält. Ich kann dann eine konkrete Klasse mit dem Namen ProductDataFlatFileParser erstellen, die die Parser-Schnittstelle implementiert, und die Analysemethode das POJO ProductData für mich füllen lassen, nachdem die Datei analysiert wurde.

Ist dieser Entwurf sinnvoll? Gibt es offensichtliche Mängel in diesem Design? Nach dem derzeitigen Stand des Designs gestatte ich den konkreten Klassen, den Algorithmus zum Parsen einer Datei zu definieren und der konkreten Klasse zu überlassen, wo die Daten gefüllt werden sollen. Das Design scheint eher von den Domänenobjekten als von den Dateiformaten abhängig zu sein. Ist das eine schlechte Sache? Alle Beiträge zur Verbesserung meines Designs werden sehr geschätzt.

CKing
quelle
Sollte die Software dem Anrufer nicht mitteilen, welche Dateiformate unterstützt werden? Woher weiß Ihre Software, welcher Parser aufgerufen werden soll?
Tomdemuyt
Sie suchen nach Feedback zu Ihrem Entwurf und nicht zu Ihrer tatsächlichen Implementierung . Daher wird dieses Feedback zu Programmierern migriert, wo es zum Thema gehört.
Codesparkle
@ Tomdemuyt Think Fabrik Muster;)
CKing
2
@bot Der SO-Benutzer, der Ihnen gesagt hat, dass Sie dies bei Code Review posten sollen, hat sich offensichtlich geirrt. Du hättest die FAQ der Site lesen können, bevor du sie postest: "Jemand hat mir gesagt, dass ich es tun soll" ist kein guter Grund für dich, etwas zu tun. Niemand spielt Ping-Pong damit. Jemand hat sich freiwillig gemeldet und versucht, einen besseren Ort dafür zu finden, anstatt ihn sofort zu schließen (was eine gültige Option gewesen wäre, da es für Code Review kein Thema ist).
Yannis
2
Bitte auch nicht kreuzen. Du machst ein Durcheinander, wir müssen aufräumen.
Ripped Off

Antworten:

7

Ich habe ein paar Bedenken:

  1. Ich würde sicherstellen, dass Sie tatsächlich ein generisches Design benötigen, bevor Sie eines implementieren. Sind Sie sicher, dass Sie andere Dateitypen als XML benötigen? Wenn nicht, warum Code für sie? Wenn Sie es irgendwann brauchen, können Sie Ihren Code an dieser Stelle nachrüsten. Es wird nicht viel länger dauern, Sie werden wahrscheinlich andere Anforderungen haben, die dazu führen, dass der Code anders aussieht als der, den Sie derzeit vorschlagen, und Sie werden ihn wahrscheinlich sowieso nie schreiben müssen. Wie sie sagen, YAGNI (Du wirst es nicht brauchen).
  2. Wenn Sie tatsächlich ein generisches Design benötigen und sich dessen ziemlich sicher sind, dann würde ich sagen, dass das Parser<T>im Grunde genommen gut ist. Ich sehe zwei potenzielle Probleme: (1) Es wird eine Dateieingabe vorausgesetzt. Was passiert, wenn Sie versuchen, einen JSON-Stream zu analysieren, den Sie beispielsweise aus einer HTTP-Antwort abgerufen haben? und (2) es liefert nicht unbedingt viel Wert, außer als Teil eines größeren generischen Frameworks, in dem Sie viele verschiedene Arten von Parsern für viele verschiedene Arten von Daten haben. Aber ich bin nicht davon überzeugt, dass Sie solch einen großen allgemeinen Rahmen brauchen. Sie haben gerade einen sehr einfachen, konkreten Anwendungsfall, soweit ich das beurteilen kann: Analysieren Sie eine XML-Datei in eine Liste von ProductDatas.
  3. Es ist fast nie eine gute Idee, Ausnahmen zu schlucken, während Sie dabei sind ProductDataXmlParser. Ich würde es RuntimeExceptionstattdessen in eine Art umwandeln .

quelle
1
Wir bauen ein Produkt, das mit vielen externen Systemen kommunizieren kann. Ich denke, es ist eine gute Idee, alle Arten von Datei- / Eingabeformaten zu berücksichtigen. Hervorragender Punkt zum JSON Stream. Das ist genau der Grund, warum meine Parser-Methode einen String-Parameter anstelle eines File-Parameters verwendet. Ich hatte einen kleinen Fehler in meinem ProductDataXmlParser, den ich korrigiert habe (muss eine Datei an den XmlBean-Parser übergeben). Sie haben auch Recht, wenn Sie Ausnahmen schlucken. Ich schrieb diesen Code schnell in Eclipse auf, um mein Design über Stackoverflow durch ein Beispiel zu vermitteln;)
CKing
OK Cool. Ich denke, ich würde den Parser-Parameter zu einem InputStream anstelle eines Strings machen, sage ich. :) Und gut zu hören über die Ausnahme - ich war mir nicht sicher, ob das aus Ihrem tatsächlichen Code oder nur aus Beispielcode für StackOverflow "n" eingefügt wurde.
1
Auch in Bezug auf die Erstellung eines Produkts, das mit vielen externen Systemen kommunizieren kann, würde ich zögern, generischen Code ohne konkrete Anforderungen zu erstellen. Zum Beispiel würde ich keine generische Parser-Schnittstelle erstellen, bis Sie mindestens zwei Arten von zu analysierenden Objekten oder zwei Dateiformate haben, die Sie benötigen.
Ich werde überlegen, was du sagst. Ich möchte darauf hinweisen, dass es 4 verschiedene XML-Dateien gibt, die 4 verschiedene Arten von Daten enthalten, die analysiert werden sollen. Produktdaten sind nur eine Art von Daten, die von unserem System / Produkt verarbeitet werden.
CKing
Ich habe noch eine Frage an Sie. Ich werde keinen Kontext verwenden, der Teil des Strategiemusters ist. Wird das in Ordnung sein? Ich werde auch die generischen Parameter und das zurückgebende Objekt in der Analysemethode in der Parser-Schnittstelle los. Dies soll verhindern, dass Klassen, die den Parser verwenden, mit einem Typparameter deklariert werden.
CKing
1

Ihr Design ist nicht die beste Option. Nach Ihrem Entwurf die einzige Möglichkeit, es zu verwenden:

ProductDataXMLTYPE parser = new ProductDataXmlParser<ProductDataXMLTYPE>().parse(input); 
ProductDataTextTYPE parser = new ProductDataTextParser<ProductDataTextTYPE >().parse(input);

Wir können nicht sehen, dass das obige Beispiel zu viel bringt. Wir können so etwas nicht machen:

Parser parser = getParser(string parserName);
parser.parse();

Sie können die folgenden zwei Optionen in Betracht ziehen, bevor Sie das Generikum suchen:

  • 1, Gleiche Ausgabe nach dem Parsen

Unabhängig davon, woher die Datenquelle stammt, haben die Produktdaten dasselbe Format, bevor Sie sie in der Datenbank speichern. Es ist der Vertrag zwischen dem Kunden und Ihrem Dump-Service. Ich gehe also davon aus, dass Sie die gleichen ProductData als Ausgabe haben. Sie können einfach eine Schnittstelle definieren:

public interface Parser {
    public ProductData parse(String inputFile);
}

Darüber hinaus definieren Sie ProductData als Schnittstelle, wenn Sie es flexibler wollen.

Wenn Sie nicht möchten, dass der Parser mit den Daten gemischt wird. Sie können es in zwei Schnittstellen aufteilen:

public interface Parser {
     public void parse(String inputFile);
}
public interface Data {
    public ProductData getData();
}

Und Ihr Parser wird so aussehen:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2, unterschiedliche Ausgabe nach dem Parsen

Wenn die ProductData nicht ähnlich sind und Sie die Parser-Schnittstelle wiederverwenden möchten. Du kannst es so machen:

public interface Parser {
   public void parse(String inputFile);
}

class XMLParse implements {
      @Override
      public void parse(String inputFile);

      ProductDataXML getProductData();        
}

class TextParse implements {
      @Override
      public void parse(String inputFile);

      ProductDataText getProductData();        
}
Canhua Li
quelle
-2

Für den Fall, dass Sie lieber etwas verwenden möchten, das bereits verfügbar ist, habe ich eine Java-Bibliothek namens JRecordBind erstellt , die auf XMLSchema basiert (unterstützt von JAXB).

Es wurde entwickelt, um Dateien mit fester Länge zu konsumieren / zu produzieren. Da XMLSchema deren Struktur definiert, können Sie es mit JAXB verwenden, um XML-Dateien zu marshallen / zu unmarshallen

Federico Fissore
quelle
Ich suche ein Design, um einen generischen Parser zu implementieren! Ich glaube nicht, dass Sie meine Frage richtig verstanden haben. :)
CKing