Posto migliore per convertire un oggetto in un altro oggetto

3

Sto scrivendo un'interfaccia per raggruppare due API sottostanti in una nuova. Le API forniscono dati sulle fatture archiviate.

Tutte e tre le API (le due vecchie e la mia nuova) hanno strutture dati diverse.
Ad esempio: <INVOICE_NO> (vecchio XML), "number" (vecchio JSON), "formatted_number" (nuovo JSON) significano tutti lo stesso campo.

Ho creato una classe per ogni API e lasciato che%'sRestTemplate di Spring gestisca l'analisi / formattazione delle risposte.

Ora, dopo aver ricevuto un oggetto ClassA dalla prima API e un oggetto ClassB dalla seconda API, devo convertirli in oggetti ClassC e restituirli nel suo formato.

Il mio primo approccio è stato quello di creare due costruttori per ClassC che prendono un oggetto di tipo ClassA o un oggetto di tipo ClassB come argomento.

Ma ora, non sono sicuro se sia la cosa giusta da fare perché sto accoppiando due oggetti di trasferimento dati.

Sarebbe meglio creare una classe InvoiceConverter o anche qualcos'altro?

    
posta das Keks 31.05.2017 - 17:16
fonte

3 risposte

3

But now I'm not sure if that's a nice thing to do because I'm coupling two data transfer objects.

Sì, sembra un odore di codice.

Would it be better to create an InvoiceConverter class or even something else?

Probabilmente sì. Se ti ho capito bene, avrai un flusso di dati come

     [class A or B object] -> [Converter] -> [C object] -> [code using C]

e Converter è l'unica posizione nel codice che dipende direttamente dalle API A e B, mentre gli oggetti C e il codice che usa gli oggetti C non dipenderanno più da A o da B (che è probabilmente il tuo obiettivo qui, per disaccoppiare le diverse parti del tuo sistema).

Se, tuttavia, si rendono gli oggetti C direttamente dipendenti da A e B e si tenta di evitare una classe di convertitore separata, tutto il codice che utilizza oggetti C dipenderà comunque da A e B, che probabilmente è qualcosa che si desidera evitare. Pensa a cosa significa quando vuoi inserire il codice usando C in una libreria separata (usando un linguaggio compilato). Nel secondo caso, questa libreria deve essere collegata alle API di A e B, mentre nel primo caso la lib non ha bisogno di un collegamento con A o B.

    
risposta data 31.05.2017 - 18:11
fonte
3

Questo mi sembra un ottimo esempio di utilizzo di Pattern adattatore

Fondamentalmente, si creerebbe un'interfaccia che avrebbe un unico metodo:

public interface Adapter
{
   ClassC ConvertObject();
}

E poi dovresti creare due classi, implementando questa interfaccia, in questo modo:

class AdapterClass : Adapter
{
   private ClassA adaptee;
   public AdapterClassA(ClassA pAdaptee)
   {
        adaptee = pAdaptee;
   }

   public ClassC ConvertObject()
   {
      //Code that converts ClassA to ClassC
   }
}

class AdapterClassB : Adapter
{
   private ClassB adaptee;
   public AdapterClassB(ClassB pAdaptee)
   {
        adaptee = pAdaptee;
   }

   public ClassC ConvertObject()
   {
      //Code that converts ClassB to ClassC
   }
}

In questo modo, stai disaccoppiando la logica della conversione del tipo in diverse classi, lasciando la stessa interfaccia alla classe utente.

Ora, si potrebbe sollevare un problema con la creazione inutilmente di un'interfaccia quando nessuno, tranne ClassC, invocherà il costruttore di classi dell'adattatore. L'interfaccia non è lì solo per chi invocherà il costruttore. Ad esempio, sarà più facile scrivere test di unità se hai qualcosa di simile.

Successivamente, potresti voler disaccoppiare completamente le classi e spostare la chiamata del costruttore su una qualche classe factory, dove ClassC vedrebbe semplicemente l'interfaccia Adapter e quindi non dipenderà in alcun modo da ClassA o ClassB. Ovviamente, questo sta flirtando con la violazione del principio KISS , ma la chiamerei una sentenza.

La linea di fondo è: se vuoi disaccoppiare ClassC da ClassA e ClassB, ci deve essere qualcosa che lega le classi dell'adattatore. Può essere una classe genitore astratta, una classe genitore normale o un'interfaccia. Considerando che l'entità genitore non ha informazioni, è logico utilizzare l'interfaccia.

    
risposta data 31.05.2017 - 17:56
fonte
0

Hai ragione, hai bisogno di un convertitore. E certo, non vuoi creare un'interfaccia specifica per ogni conversione, ecco a cosa servono i generici. Non sono sicuro della sintassi in java, ma sono abbastanza sicuro è fattibile come in C #:

Per prima cosa creerò un'interfaccia per gestire le conversioni da un tipo all'altro:

public interface IObjectConverter<TSource, TResult>{
       TResult Convert(TSource source);
}

Ma non vuoi fare riferimento a questo per ogni conversione che vuoi creare, per questo creeremo un'altra interfaccia, come questa:

public interface IConverter(){
       TResult Convert<TSource,TResult>(TSource source);

       IObjectConverterFactory Factory;
}

Si noti che qui abbiamo un'altra interfaccia, IObjectConverterFactory. Lo userai nell'implementazione di IConverter.Convert per ottenere l'oggetto IObjectConverter. La fabbrica è così:

public interface IConverterFactory
{
    IObjectConverter<TSource, TResult> Create<TSource, TResult>();
}

Quindi abbiamo 3 interfacce, che potrebbero sembrare molte, ma l'idea qui è di facilitare l'uso (e non aumentare l'accoppiamento). Dovrai solo fare riferimento a IConverter e dire al metodo Converti il tipo di sorgente e il tipo di risultato.

Dichiarerai solo:

IConverter _converter;

E usalo in questo modo:

var classC1 = _converter.Convert<ClassA,ClassC>(classA);

var classC2 = _converter.Convert<ClassB,ClassC>(classB);

Da lì devi solo preoccuparti delle specifiche implementazioni dei convertitori.

    
risposta data 01.06.2017 - 23:13
fonte

Leggi altre domande sui tag