Necesito desarrollar una API, las funciones de la API son solicitudes que llaman al servicio expuesto por un servidor.
Inicialmente, la API funcionó así:
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')
Había mucho código duplicado y no me gustó la forma en que pasó los argumentos de la solicitud. Debido a que hay muchos argumentos, quería extraerlos de la solicitud y hacer algo más paramétrico.
Entonces refactoré de esta manera:
# 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)
El patrón de estrategia funcionó, la duplicación se elimina. Independientemente de esto, me temo que tengo cosas complicadas, especialmente en lo que respecta a los argumentos, no me gusta la forma en que los manejo, porque cuando miro el código no parece fácil y claro.
Así que quería saber si hay un patrón o una forma mejor y más clara de lidiar con este tipo de código API del lado del cliente.
Respuestas:
Reconsideraría usar un diccionario (hash, mapa, lo que sea que su idioma llame un conjunto de pares clave / valor) para los argumentos. Hacerlo de esa manera hace imposible que el compilador verifique si la persona que llama ha incluido todos los valores necesarios. A los desarrolladores les resulta difícil determinar si tienen todos los argumentos necesarios. Hace que sea fácil incluir accidentalmente algo que no necesitaba y olvidar algo que sí necesitaba. Y termina teniendo que poner todos los valores en el diccionario al llamar, y tener que verificar el diccionario en cada función para extraer todos los argumentos, lo que aumenta la sobrecarga. El uso de algún tipo de estructura especializada puede reducir el número de argumentos sin reducir la capacidad de los compiladores para verificarlos y la capacidad de los desarrolladores para ver claramente lo que se necesita.
fuente
Creo que la API de su servidor debería tener tantas entradas como solicitudes requeridas. Por lo tanto, cualquier desarrollador podrá leer la API fácilmente ( consulte el enrutamiento de lask como ejemplo ).
Para evitar la duplicación en el código, puede usar métodos internos
fuente
API, arquitectura y patrón tienen que ver con la comunicación y la intención. Su segunda versión me parece bastante simple y parece algo extensible también, pero mi opinión (o incluso la suya) no es lo que importa aquí.
Obtenga comentarios
Mire su software de afuera hacia adentro (no de adentro hacia afuera). Si tiene un sitio web, comience desde allí. Espere encontrar fácilmente una y solo una forma obvia de hacer lo que se espera que haga su software. Encuentre a alguien en el pasillo y solicite comentarios sobre la usabilidad.
Identificar el objeto comercial principal
Dado que la programación de software siempre se trata de hacer algo nuevo a partir de dos cosas diferentes, tener una
Server
que me tomeRequest
parece razonable. ElServer
probablemente requerirá de configuración y tiene los valores iniciales adecuados. Probablemente proporcione algo como un singleton o una fábrica para facilitar su uso. Las cosas interesantes reales suceden en elRequest
. Sus clientes solo tienen que asegurarse de construir elRequest
objeto adecuado . Haga su intención obvia y su negocio claro.Estar abierto para extensión, pero cerrado para modificación
Podría hacerlo aún más simple codificando diferentes comportamientos utilizando la herencia en lugar de múltiples métodos públicos en el
Request
objeto, más o menos como en el patrón de comando . De esta manera, los clientes también pueden escribir sus propias solicitudes, y los complementos pueden proporcionar nuevas solicitudes (por ejemplo, utilizando los puntos de entrada de setuptools) si es necesario. Esto también aseguraría que elRequest
objeto nunca se convierta en una clase de dios , o que se cambie su API si se agregan nuevas características.fuente