Come creare più convertitori

0

Sto creando un software che dovrebbe accettare una determinata forma di dati e qualcosa nei dati. Abbiamo deciso di creare in anticipo una piccola dipendenza che verrà utilizzata per rendere più fluido lo sviluppo del software. Stiamo pianificando di accettare il supporto per i file json, csv, yaml e sql / url (che consumano il resto) e convertirli in elenchi ( ArrayList ). Per ora, abbiamo il supporto per JSON. Abbiamo creato un genitore Data Reader che contiene due contratti di metodo getData , uno per i caricamenti di file e uno per i collegamenti url: ognuno restituisce json convertito in un elenco. Questo è implementato da JsonDataReader . Funziona e perfettamente utilizzabile. Tuttavia, ho questo come codice per il software:

list = JsonDataReader().getData(new File("file"));

Questo lo vedo come un problema. Se il mio software ha questo come codice, significa che accetterà solo severamente Json. Cosa succede se creiamo il YamlDataReader , CsvDataReader e molto altro? C'è un modo migliore per progettare il convertitore multiplo che sarà implementabile per lo sviluppo del software?

Processo / Flusso del software

  1. Carica il file dalla pagina di caricamento
  2. Il file può ora essere trovato nella directory del server
  3. Nella pagina di elaborazione dati, il file viene acquisito e convertito nell'elenco come mostrato nel codice sopra.

Sarebbe meglio se creassi un componente che accetti qualsiasi dato, rilevi il tipo di file, lo converta e lo restituisca come lista?

Il mio collega ha suggerito questo.

What you could do is you have something that stores all of your data readers in a map and the key is the file extension it basically then gets from the Map the appropriate DataReader based on the file type

È possibile? Per memorizzare DataReaders come valori? I DataReader sono i convertitori.

Il mio collega ha anche sottolineato che se andassi con DataReader that accepts anything and returns a list sarebbe un caso di comportamenti multipli e non di polimorfismo - indicando un cattivo design.

    
posta Rei Brown 14.02.2018 - 00:55
fonte

2 risposte

1

Dato che hai un "lettore genitore" della classe genitrice, il tuo codice client non dovrebbe usare un'implementazione, ma piuttosto la classe genitore. Inoltre è necessario un metodo per trovare l'implementazione corretta per un determinato file. Può essere fatto da una mappa se si desidera memorizzare i lettori tra le chiamate. Ad ogni modo il tuo codice cliente dovrebbe essere simile a:

ParentReader reader = ParentReaderFactory.getReader(fileExtension);
List yourList = reader.getData(yourFile);

In questo modo il tuo cliente non deve conoscere le tue implementazioni dirette (json, URL ecc.) Ecco perché hai creato la classe genitore.

Uno dei vantaggi di questo modello che non devi affrontare estensioni e tipi di file nel codice client (nel codice sopra). Cosa succede all'interno della fabbrica? È un business diverso. Quindi hai separato due diverse preoccupazioni in due luoghi diversi.

Userai diverse istruzioni if nella tua fabbrica? Sono i dettagli della tua implementazione (ma non è necessario), quindi al tuo cliente non interessa.

In fabbrica devi controllare il flusso di esecuzione in qualche modo. Può essere if-else, cambiare, usare una mappa o qualcos'altro.

    
risposta data 14.02.2018 - 06:13
fonte
0

Ho lavorato a un progetto che doveva analizzare una vasta gamma di tipi di file con formati molto diversi tra cui CSV, Excel, XML, SQLite, Access, formati proprietari, ecc. Visto che dovevamo affrontare anche diverse versioni di formati proprietari avevamo bisogno di un po 'più di flessibilità. Avevamo il concetto di Processor e l'output stava aggiornando il nostro database.

L'interfaccia per i nostri processori era simile a questa:

IProcessor
{
    bool CanProcess(FileInfo file);

    Task Process(FileInfo file, IProgress<ParseProgress>);
}

Abbiamo usato un semplice elenco che ordinava i nostri processori in modo che le istanze più specifiche fossero le prime e gli articoli più generici fossero gli ultimi. La ragione di ciò è che in alcuni casi dovevamo raggiungere il picco all'interno del file. La determinazione del processore appropriato era semplice quanto l'equivalente:

 var processor = RegisteredProcessors.FirstOrDefault(p => p.CanProcess(file));

 if (processor == null) {
      throw new NotSupportedException("Cannot process file: " + file.FullName);
 }

In altre parole, il primo processore che ha detto di sapere come gestire quel file. Per i processori generici, dovevamo solo controllare l'estensione. Per i formati proprietari abbiamo avuto bisogno di ispezionare la struttura ed estrarre le informazioni sulla versione del formato.

NOTA: IProgress<T> è un idioma C # per riportare lo stato utilizzando una struct, dal momento che disponevamo di un'applicazione desktop per mostrare dove eravamo nell'elaborazione. Alcuni file erano piuttosto grandi. Se non hai quel bisogno, non ti preoccupare. Anche il tipo di ritorno Task è un idioma C # per consentire al codice chiamante di utilizzare async e await parole chiave. Utilizza un tipo di reso che renda valida la tua lingua / l'architettura dell'applicazione.

    
risposta data 14.02.2018 - 19:20
fonte

Leggi altre domande sui tag