Refactoring di un'API client per evitare il codice duplicato e il passaggio non chiaro dei parametri

8

Ho bisogno di sviluppare un'API, le funzioni dell'API sono richieste che chiamano il servizio esposto da un server.

Inizialmente l'API funzionava in questo modo:

class Server:
    def firstRequest(self, arg1, arg2):
        # block of code A
        async = Async()
        async.callFirstRequest(arg1, arg2)
        # block of code B

    def secondRequest(self, argA, argB, argC):
        # block of code A (identical to that of firstRequest)
        async = Async()
        async.callSecondRequest(argA, argB, argC)
        # block of code B (identical to that of firstRequest)

class Async:
    def callFirstRequest(self, arg1, arg2):
        doFirstRequest(arg1, arg2)

    # run the real request and wait for the answer
    def doFirstRequest(self, arg1, arg2):
        response = client.firstRequest(arg1, arg2)

    def callSecondRequest(self, argA, argB, argC):
        doSecondRequest(argA, argB, argC)

    # run the real request and wait for the answer
    def doSecondRequest(self, argA, argB, argC):
        response = client.secondRequest(argA, argB, argC)

server = Server()
server.firstRequest(arg1=1, arg2=2)
server.secondRequest(argA='A', argB='B', argC='C')

C'era un sacco di codice duplicato e non mi piaceva il modo in cui passava gli argomenti per la richiesta. Perché ci sono molti argomenti quindi volevo estrarli dalla richiesta e creare qualcosa di più parametrico.

Così mi sono refactored in questo modo:

# using a strategy pattern I was able to remove the duplication of code A and code B
# now send() receive and invoke the request I wanna send
class Server:
    def send(self, sendRequest):
        # block of code A
        asynch = Async()
        sendRequest(asynch)
        # block of code B

# Request contains all the requests and a list of the arguments used (requestInfo)
class Request:
    # number and name of the arguments are not the same for all the requests
    # this function take care of this and store the arguments in RequestInfo for later use
    def setRequestInfo(self, **kwargs):
        if kwargs is not None:
            for key, value in kwargs.iteritems():
                self.requestInfo[key] = value

    def firstRequest(async)
        async.doFirstRequest(self.requestInfo)

    def secondRequest(async)
        async.doSecondRequest(self.requestInfo)

# Async run the real request and wait for the answer
class Async:
    def doFirstRequest(requestInfo):
        response = client.firstRequest(requestInfo['arg1'], requestInfo['arg2'])

    def doSecondRequest(requestInfo)
        response = client.secondRequest(requestInfo['argA'], requestInfo['argB'], requestInfo['argC'])  


server = Server()
request = Request()

request.setRequestInfo(arg1=1, arg2=2) # set of the arguments needed for the request
server.send(request.firstRequest)

request.setRequestInfo(argA='A', argB='B', argC='C')
server.send(request.secondRequest)

Il modello di strategia ha funzionato, la duplicazione è stata rimossa. A prescindere da questo, ho paura di avere cose complicate, soprattutto riguardo gli argomenti, non mi piace il modo in cui li gestisco, perché quando guardo il codice non appare facile e chiaro.

Quindi volevo sapere se esiste un modello o un modo migliore e più chiaro per gestire questo tipo di codice API lato client.

    
posta k4ppa 18.12.2015 - 10:11
fonte

3 risposte

1

Vorrei riconsiderare l'uso di un dizionario (hash, mappa, qualunque sia la lingua che chiama un insieme di coppie chiave / valore) per gli argomenti. Facendolo in questo modo è impossibile per il compilatore verificare se un chiamante ha incluso tutti i valori necessari. Rende difficile per lo sviluppatore utilizzarlo per capire se hanno tutti gli argomenti necessari. Rende facile includere accidentalmente qualcosa che non ti serve e dimenticare qualcosa di cui hai bisogno. E si finisce per dover mettere tutti i valori nel dizionario quando si chiama, e dover controllare il dizionario in ogni funzione per estrarre tutti gli argomenti, aumentando il sovraccarico. L'utilizzo di una sorta di struttura specializzata può ridurre il numero di argomenti senza ridurre la capacità dei compilatori di controllarli e gli sviluppatori possono vedere chiaramente cosa è necessario.

    
risposta data 28.08.2017 - 07:12
fonte
0

Penso che l'API del tuo server dovrebbe avere tante voci quante richieste richieste. Pertanto, qualsiasi sviluppatore sarà in grado di leggere facilmente l'API ( vedere il routing del lask come esempio ).

Per evitare la duplicazione nel codice, puoi utilizzare i metodi interni

    
risposta data 04.04.2016 - 16:22
fonte
0

API, architettura e pattern sono tutti di comunicazione e intenti. La tua seconda versione sembra abbastanza semplice per me e sembra anche molto estensibile, ma la mia opinione (o anche la tua) non è ciò che conta qui.

Ottieni feedback

Guarda il tuo software da fuori (non dentro). Se ha un sito web, inizia da lì. Aspettatevi di trovare facilmente un solo e ovvio modo di fare ciò che il vostro software dovrebbe fare. Trova qualcuno nel corridoio e chiedi feedback sull'usabilità.

Identifica l'oggetto di business principale

Dato che la programmazione del software consiste sempre nel creare qualcosa di nuovo da due cose diverse, avere un Server che richiede un Request mi sembra sensato. Il Server probabilmente richiede la configurazione e ha impostazioni predefinite sensate. Probabilmente fornisci qualcosa come un singleton o una fabbrica per facilitare il suo utilizzo. Le cose reali che si intersecano si verificano in Request . I tuoi clienti devono solo assicurarsi di creare l'oggetto Request corretto. Rendi chiaro il tuo intento e deseleziona la attività .

Sii aperto per l'estensione, ma chiuso per la modifica

Potresti renderlo ancora più semplice codificando comportamenti diversi usando l'ereditarietà invece di più metodi pubblici nell'oggetto Request , più o meno come nel schema di comando . In questo modo, i clienti potevano anche scrivere le proprie richieste e nuove richieste potevano essere fornite dai plugin (usando, ad esempio, i punti di ingresso di setuptools), se necessario. Ciò assicurerebbe anche che l'oggetto Request non diventi mai una classe di Dio , o che la sua API venga modificata se vengono aggiunte nuove funzionalità .

    
risposta data 22.04.2018 - 03:03
fonte