Come creare dinamicamente le chiamate di riposo per interfacciarsi con diverse API?

2

Diciamo che sto costruendo un'applicazione in cui i clienti possono prenotare i biglietti dell'autobus. L'unica cosa che fanno è selezionare, origine, destinazione e il tipo di servizio (espresso o standard). Il sistema quindi prenoterà il biglietto contro la corretta compagnia di autobus api. L'utente non ha bisogno di scegliere il fornitore di servizi in quanto è già stato negoziato dal gestore dell'hotel con le diverse compagnie di autobus.
Lo scopo principale è evitare di modificare il codice esistente se un nuovo direttore dell'hotel desidera aggiungere, rimuovere o modificare la compagnia di autobus per un fornitore di servizi.

Ad esempio:

consumer: Hotel 1  
type of service: express  
service provider: Bus Co. A

consumer: Hotel 1   
type of service: standard  
service provider: Bus Co. B

consumer: Hotel 2  
type of service: express  
service provider: Bus Co. A

...

Ogni provider ha la propria API per la prenotazione e ho bisogno di generare la chiamata e il carico utile in fase di esecuzione.

Questo è quello che ho pensato di fare:

OPZIONE 1 Memorizza i dettagli delle chiamate di riposo come modello in una tabella di database come stringa con i segnaposto

contract (hotel name, type of service, api call url, payload)

Il payload verrebbe memorizzato come testo con i segnaposto.

Esempio:
informazioni sulla tabella contrat:

"Hilton New York", "express", "http://api.buscoA.com/book-ticket", "{origin:<origin>, destination:<destination>, service: expr}"
"Hilton New York", "standard", "http://api.busB.com/book", "{start:<origin>, end:<destination>, service_type: standard}"

OPZIONE 2 Memorizza i dettagli delle chiamate di riposo come modello in una tabella di database e crea un'istanza della chiamata di resto utilizzando una classe tramite la riflessione

contract (hotel name, type of service, api call url, api class name)

E verrà creata una classe per ogni API e io posso istanziare e posso istanziarla attraverso il reflection usando il valore "class name" della tabella

Esempio:

"Hilton New York", "express", "http://api.buscoA.com/book-ticket", "BusCompanyAApi"

Quindi dovrò creare una classe per ogni API e istanziare l'oggetto su richiesta per riflessione usando il nome della classe memorizzato nel database (BusCompanyAApi)

  • L'opzione 1 o 2 è un modo valido per farlo?
  • Esiste uno schema di progettazione che potrei usare?

NOTA: sto usando java

    
posta Manuel Sopena Ballesteros 26.08.2018 - 02:31
fonte

3 risposte

1

Perché non dovresti semplicemente usare una mappa dei fornitori di servizi per nome? Sarebbe come lo schema di comando:

HotelServices.get(serviceProviderName)
    .handleService(serviceName, consumer);
    
risposta data 26.08.2018 - 03:46
fonte
1

Is this the right way to do this?

È fattibile ma potresti finire con un codice complicato se le API sono troppo diverse e anche i formati di risposta sono molto diversi.

Is there a design pattern I could use?

Potresti considerare Strategy Pattern, che ti permetterebbe di astrarre un'interfaccia per un fornitore generico e di implementare dettagli di richiesta e risposta in sottoclassi concrete. Quindi puoi aggiungere nuovi servizi senza disturbare il tuo codice principale.

Can I use reflection to build the payload?

Reflection è un modo per esaminare i dettagli di classi e oggetti durante l'esecuzione. Tuttavia, in Java è possibile utilizzare la reflection per istanziare i provider in base al nome del provider letto dal database. È meglio che utilizzare le istruzioni switch / if-else e il nuovo operatore contro i nomi dei provider. Ad esempio:

Object provider = Class.forName("ProviderA").newInstance();  
    
risposta data 26.08.2018 - 04:24
fonte
0

Opzioni di design

Se tutte le API del fornitore sono di natura molto simile e semplici come inviare un paio di attributi per effettuare la prenotazione + analizzando la risposta, l'opzione 1 è sicuramente la strada da seguire.

Ma se l'API potrebbe essere molto diversa e più complessa (ad esempio multi-step, con diversi tipi di autenticazione o utilizzando diversi protocolli di comunicazione), avresti certamente un approccio più elaborato. L'idea sarebbe quindi:

  • utilizza un builder specifico per API per generare un insieme di chiamate API
  • le chiamate sarebbero implementate utilizzando il schema di comando e quindi il passaggio finale del builder richiama i comandi nel giusto ordine.
  • API diverse potrebbero richiedere classi di comando completamente diverse. Pertanto, puoi utilizzare il modello di fabbrica astratto , per creare famiglie di comandi correlati.

In alternativa, puoi semplicemente immaginare di utilizzare un motore di orchestrazione del servizio esistente.

riflessione

La riflessione è un design molto attraente. Ma il software auto-introspettivo è pieno di pericoli . Consiglierei di non usarlo:

  • Reflection creerebbe un accoppiamento non necessario tra i dati e il codice.
  • Un nuovo sviluppatore (o il tuo io futuro in 5 anni?) potrebbe perdere questa dipendenza esterna e rinominare la classe.
  • Per evitare che un utente inserisca un nome di classe scadente o che un hacker sfrutti questo progetto per filtrare gli attributi sensibili nell'oggetto cutstomer (numero di carta di credito?), un sanitizzazione dei dati saranno comunque necessari (all'entrata dell'utente o appena in tempo prima di utilizzare i dati).

La convalida dei dati richiesta dalla riflessione richiederebbe comunque un elenco di stringhe consentite. Quindi, perché non mantenere questa lista in una mappa che potrebbe trovare un oggetto in grado di creare la classe giusta o riempire l'attributo giusto (probabilmente usando alcune varianti del pattern di comando)?

IMHO, questa alternativa di mappe e comandi non sarebbe fantastica quanto il riflesso, ma renderebbe il tuo sistema più affidabile.

    
risposta data 26.08.2018 - 13:00
fonte

Leggi altre domande sui tag