Progettazione di parser di file generici in Java utilizzando il modello di strategia


14

Sto lavorando a un prodotto in cui la responsabilità di uno dei moduli è analizzare i file XML e scaricare il contenuto richiesto in un database. Anche se il presente requisito è solo di analizzare i file XML, voglio progettare il mio modulo di analisi in modo da poter supportare qualsiasi tipo di file in futuro. La ragione di questo approccio è che stiamo costruendo questo prodotto per un cliente specifico ma prevediamo di venderlo ad altri clienti nel prossimo futuro. Tutti i sistemi nell'ecosistema per il client corrente producono e consumano file XML ma questo potrebbe non essere il caso per altri client.

Cosa ho provato finora? (Il presente) Ho in mente il seguente disegno basato sul modello di strategia. Ho rapidamente scritto il codice in eclipse per trasmettere il mio design, quindi sarebbe fantastico se per il momento ignorassimo altri aspetti come il modo corretto di gestire le eccezioni.

Parser: l'interfaccia strategica che espone un metodo di analisi.

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

* Il motivo dell'utilizzo di un parametro generico è consentire qualsiasi tipo di ritorno e garantire la sicurezza del tipo al momento della compilazione.

ProductDataXmlParser Una classe concreta per l'analisi di un file product.xml che contiene informazioni relative al prodotto. (usando 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();
    }
} 

dove : ProductDataTYPE e ProductDataDocument sono classi POJO XMlBean generate utilizzando un comando xsd e scomp.

Il futuro

Se avrò un file product.txt da analizzare in futuro, posso definire il mio POJO chiamato ProductData che conterrà i contenuti richiesti del file. Posso quindi creare una classe concreta denominata ProductDataFlatFileParser che implementa l'interfaccia del parser e che il metodo di analisi compili il POJO ProductData per me dopo aver analizzato il file.

Questo design ha senso? Ci sono difetti evidenti in questo design? Allo stato attuale del progetto, sto permettendo alle classi concrete di definire l'algoritmo per analizzare un file e lasciando che la classe concreta decida dove popolare i dati. Il design sembra essere più dipendente dagli oggetti di dominio piuttosto che dai formati di file. è una cosa negativa? Qualsiasi input su come posso migliorare il mio design sarà molto apprezzato.


Il software non dovrebbe far sapere al chiamante quali formati di file sono supportati? Come fa il tuo software a sapere quale parser deve invocare?
tomdemuyt,

Stai cercando feedback sul tuo design , non sulla tua effettiva implementazione , quindi questo verrà migrato ai programmatori dove è in argomento.
codesparkle del

@tomdemuyt Think modello di fabbrica;)
CKing

2
@bot L'utente SO che ti ha detto di pubblicare questo su Code Review era ovviamente sbagliato. Avresti potuto leggere le FAQ del sito prima di pubblicarlo, "qualcuno mi ha detto di farlo" non è davvero un buon motivo per fare qualcosa. Nessuno ci gioca a ping pong, qualcuno si è offerto volontario e ha cercato di trovare un posto migliore per farlo invece di chiuderlo completamente (che sarebbe stata un'opzione valida, dato che è fuori tema per Code Review).
yannis,

2
Per favore, non fare il crosspost neanche. Stai facendo un casino che dobbiamo ripulire.
Strappato il

Risposte:


7

Ho un paio di preoccupazioni:

  1. Mi assicurerei che tu abbia effettivamente bisogno di un progetto generico prima di implementarne uno. Sei sicuro di aver bisogno di tipi di file diversi da XML? In caso contrario, perché codice per loro? Se alla fine ne hai bisogno, puoi adattare il tuo codice a quel punto. Non ci vorrà molto più tempo, probabilmente avrai altri requisiti che renderanno il codice diverso da quello che stai proponendo e probabilmente non dovrai mai scriverlo comunque. Come si suol dire, YAGNI (non ne avrai bisogno).
  2. Se in realtà hai bisogno di un design generico e ne sei abbastanza sicuro, direi che Parser<T>è fondamentalmente sano. Vedo due potenziali problemi: (1) presuppone l'input del file - cosa succede se stai cercando di analizzare un flusso JSON che hai recuperato da una risposta HTTP, ad esempio? e (2) non fornisce necessariamente molto valore se non come parte di un più ampio framework generico in cui si hanno molti tipi diversi di parser per molti tipi diversi di dati. Ma non sono convinto che tu abbia bisogno di un framework generico così ampio. In questo momento hai solo un caso d'uso molto semplice e concreto, per quanto ne so: analizzare un file XML in un elenco di ProductDatas.
  3. Non è quasi mai una buona idea inghiottire le eccezioni mentre stai facendo ProductDataXmlParser. Vorrei convertirlo in una sorta di RuntimeExceptioninvece.

1
Stiamo costruendo un prodotto in grado di comunicare con molti sistemi esterni, quindi suppongo che sarebbe una buona idea tenere conto di qualsiasi tipo di file / formato di input. Punto eccellente sul flusso JSON. Questo è esattamente il motivo per cui il mio metodo di analisi nell'interfaccia del parser utilizzava un parametro String anziché un parametro File. Ho avuto un piccolo errore nel mio ProductDataXmlParser che ho corretto (è necessario passare un file al parser XmlBean). Hai anche ragione a ingoiare le eccezioni. Ho scritto rapidamente questo codice in eclissi per trasmettere il mio progetto su StackOverflow attraverso un esempio;)
CKing

Ok bello. Immagino che farei del parametro Parser un InputStream anziché una stringa, è quello che sto dicendo. :) E bello sapere dell'eccezione: non ero sicuro che fosse tagliato e incollato dal tuo codice attuale o solo dal codice di esempio per StackOverflow.

1
Inoltre, per quanto riguarda la costruzione di un prodotto in grado di comunicare con molti sistemi esterni, esiterei a creare qualsiasi codice generico senza requisiti concreti. Ad esempio, finché non hai almeno due tipi di oggetto da analizzare, o due formati di file, di cui hai bisogno, non creerei un'interfaccia di parser generica.

Ci penserò su quello che stai dicendo. Vorrei sottolineare che ci sono 4 diversi file XML contenenti 4 diversi tipi di dati da analizzare. I dati di prodotto sono solo un tipo di dati che possono essere consumati dal nostro sistema / prodotto.
CKing

Ho un'altra domanda per te. Non userò un contesto che fa parte del modello di strategia. Andrà tutto bene? Mi sto anche sbarazzando dei parametri generici e restituendo Object nel metodo di analisi nell'interfaccia del parser. Questo per evitare che le classi che usano il parser vengano dichiarate con un parametro di tipo.
CKing

1

Il tuo design non è l'opzione migliore. Secondo il tuo design, l'unico modo per usarlo:

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

Non possiamo vedere troppi benefici dall'esempio sopra. Non possiamo fare cose del genere:

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

Puoi considerare le due opzioni seguenti prima di cercare il generico:

  • 1, stessa uscita dopo l'analisi

Indipendentemente da dove provenga l'origine dati, i dati del prodotto avranno lo stesso formato prima di salvarli nel database. È il contratto tra il cliente e il tuo servizio di dump. Quindi suppongo che tu abbia gli stessi ProductData dell'output. Puoi semplicemente definire un'interfaccia:

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

Inoltre definisci ProductData come interfaccia se vuoi che sia più flessibile.

Se non si desidera che il parser sia mischiato ai dati. Puoi dividerlo in due interfacce:

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

E il tuo parser sarà simile a questo:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2, output diverso dopo l'analisi

Se ProductData non è simile e si desidera riutilizzare l'interfaccia del parser. Puoi farlo in questo modo:

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

-2

Nel caso in cui preferirai usare qualcosa di già disponibile, ho creato una libreria java chiamata JRecordBind basata su XMLSchema (supportato da JAXB).

È nato per consumare / produrre file a lunghezza fissa e, poiché XMLSchema ne definisce la struttura, è possibile utilizzarlo con JAXB semplice per eseguire il marshalling / unmarshall dei file XML


Sto cercando un progetto per implementare un parser generico! Non credo che tu abbia capito bene la mia domanda. :)
CKing
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.