Refactoring di un client che chiama un servizio Rest

2

Ho una classe incaricata di chiamare un servizio e restituire una risposta.

Il contratto è come questo

interface ServiceClient {
    ServiceResponse process(ServiceSubmissionParams params);
}

ServiceSubmissionParams e ServiceResponse sono semplici POJO che fanno parte dell'applicazione che chiama il servizio (e non fa parte dell'API di servizio).

ServiceSubmissionParams ha un gruppo di membri di dati necessari per chiamare un'API del servizio e ServiceResponse ha dati che interpretati dalla risposta del servizio.

La classe di implementazione è così

class ServiceClientImpl implements ServiceClient {
     private Client client; //JAXRS client

     ServiceResponse process(ServiceSubmissionParams params) {
         //1
         HttpHeaders headers = createHeaders(params);
         //2
         Response response = //call service with the above headers
         //3 - Build reponse
         if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
           // Build ServiceResponse
         }
         //4 
         throw handleErrorResponse(response);
    }
    HttpHeaders createHeaders(ServiceSubmissionParams params) {
      //Builds HttpHeaders from ServiceSubmissionParams fields
    } 

    RuntimeException handleACAPIErrorResponse(Response response) {
     /*
        Interprets the response to throw a (custom) subclass of RuntimeException
      if response == 503, return ServiceUnavailableException
      if response == 408, return RequestTimeoutException
      .... 
      if response == 500, return ServerException

     else return UnknownException
     */
    }

}

La ragione per lanciare RuntimeException specifiche (o diverse) è quella basata sul tipo che alcuni vengono ripetuti (ServiceUnavailable o RequestTimeout) mentre altri non lo fanno.

La mia domanda:

Sento che questa classe ha un sacco di lavoro. Prepara intestazioni, chiama il servizio, interpreta la risposta. Quindi, viola l'SRP? Oppure funziona solo considerando il livello di astrazione di prendere un ServiceSubmissionParams e restituire un ServiceResponse .

Più di una violazione di SRP, ho bisogno di cambiare questa classe ogni volta che ho bisogno di gestire una risposta di errore dal servizio cioè, ho bisogno di leggere il corpo della risposta in caso di codice di risposta 500 o 400 e interpretarlo per popolare un %codice%. Quindi, dovrei prendere in considerazione la divisione di questa classe in più classi?

    
posta user7 21.08.2018 - 07:52
fonte

1 risposta

2

Di solito ci sono diversi modi di considerare le responsabilità che una classe ha, nel contesto dell'SRP. Se consideri che la sua responsabilità è "incapsulare le chiamate di servizio REST, in modo che i clienti di questa classe non debbano preoccuparsi di HTTP & co", che è OK nel mio libro anche se piuttosto ampio, quindi avere una singola classe non è grande affare - ammesso che non si tratti di un'enorme API che si traduce in un client di centinaia se non di migliaia di righe.

Ricorda che l'SRP riguarda esclusivamente la riduzione dell'accoppiamento, ma il codice dovrebbe rimanere coeso, cioè gli elementi correlati dovrebbero essere vicini l'uno all'altro.

Se la classe è così grande che lavorare con essa è difficile, allora ha senso cercare di dividerla. In questo caso, la responsabilità sopra descritta diventa quella del modulo (parlo qui di un modulo concettuale, non di uno Java 9) di cui le classi faranno parte, con ogni classe all'interno che ha una responsabilità più precisa.

Con il problema di dover gestire molte risposte diverse, un modo per farlo dovrebbe definire un'interfaccia ResponseHandler con implementazioni che gestiscono ciascuna un singolo caso e la classe di implementazione del client deve essere configurata per utilizzare un dato ResponseHandler a seconda dello stato della risposta (idealmente configurato in Map<HttpStatus, ResponseHandler> in modo che tu non debba gestire una catena di ifs nel codice, o in un List|Set<ResponseHandler> con ogni gestore che definisce un metodo canHandle(Response) se determinare quale utilizzare non è così semplice come controllare semplicemente il codice di stato). In questo modo, gestire un nuovo caso sarebbe semplice come aggiungere una nuova implementazione ResponseHandler e aggiornare l'elenco di oggetti utilizzati da ServiceClientImpl .

    
risposta data 21.08.2018 - 10:51
fonte

Leggi altre domande sui tag