Ich muss eine API entwickeln. Die Funktionen der API sind Anforderungen, die den von einem Server bereitgestellten Dienst aufrufen.
Anfangs funktionierte die API folgendermaßen:
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')
Es gab viel duplizierten Code und mir hat nicht gefallen, wie die Argumente für die Anfrage übergeben wurden. Da es viele Argumente gibt, wollte ich sie aus der Anfrage extrahieren und etwas parametrischeres machen.
Also habe ich folgendermaßen umgestaltet:
# 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)
Das Strategiemuster hat funktioniert, die Duplizierung wird entfernt. Unabhängig davon habe ich Angst, komplizierte Dinge zu haben, insbesondere in Bezug auf die Argumente. Ich mag es nicht, wie ich damit umgehe, denn wenn ich mir den Code anschaue, erscheint das nicht einfach und klar.
Ich wollte wissen, ob es ein Muster oder eine bessere und klarere Möglichkeit gibt, mit dieser Art von clientseitigem API-Code umzugehen.
Antworten:
Ich würde die Verwendung eines Wörterbuchs (Hash, Map, wie auch immer Ihre Sprache eine Reihe von Schlüssel / Wert-Paaren nennt) für die Argumente überdenken. Auf diese Weise kann der Compiler nicht überprüfen, ob ein Aufrufer alle erforderlichen Werte enthalten hat. Es macht es dem Entwickler schwer, herauszufinden, ob er alle erforderlichen Argumente hat. Es macht es einfach, versehentlich etwas einzuschließen, das Sie nicht brauchten, und etwas zu vergessen, das Sie brauchten. Und am Ende müssen Sie beim Aufrufen alle Werte in das Wörterbuch einfügen und das Wörterbuch in jeder Funktion überprüfen, um alle Argumente zu extrahieren, was den Overhead erhöht. Die Verwendung einer speziellen Struktur kann die Anzahl der Argumente verringern, ohne dass die Compiler diese überprüfen und die Entwickler klar erkennen können, was benötigt wird.
quelle
Ich denke, Ihre Server-API sollte so viele Einträge haben, wie erforderlich sind. Somit kann jeder Entwickler die API leicht lesen ( siehe Lask-Routing als Beispiel ).
Um Doppelungen im Code zu vermeiden, können Sie interne Methoden verwenden
quelle
Bei API, Architektur und Muster dreht sich alles um Kommunikation und Absicht. Ihre zweite Version sieht für mich einfach genug aus und scheint auch etwas erweiterbar zu sein, aber meine Meinung (oder sogar Ihre) ist hier nicht wichtig.
Erhalten Sie Feedback
Schauen Sie sich Ihre Software von außen nach innen an (nicht von innen nach außen). Wenn es eine Website gibt, starten Sie von dort. Erwarten Sie, dass Sie leicht einen und nur einen offensichtlichen Weg finden, um das zu tun, was von Ihrer Software erwartet wird. Finden Sie jemanden auf dem Flur und bitten Sie um Feedback zur Benutzerfreundlichkeit.
Identifizieren Sie das Hauptgeschäftsobjekt
Da es bei der Softwareprogrammierung immer darum geht, aus zwei verschiedenen Dingen etwas Neues zu machen
Server
,Request
erscheint es mir sinnvoll , eine zu haben , die eine erfordert. DieServer
erfordern wahrscheinlich Konfiguration und haben sinnvolle Standardeinstellungen. Sie stellen wahrscheinlich so etwas wie einen Singleton oder eine Factory zur Verfügung, um die Verwendung zu vereinfachen. Das wirklich interessante Zeug passiert in derRequest
. Ihre Kunden müssen nur sicherstellen, dass das richtigeRequest
Objekt erstellt wird. Machen Sie Ihre Absicht offensichtlich und Ihr Geschäft klar.Seien Sie offen für Erweiterungen, aber geschlossen für Änderungen
Sie können es noch einfacher machen, indem Sie verschiedene Verhaltensweisen mithilfe der Vererbung anstelle mehrerer öffentlicher Methoden im
Request
Objekt codieren , ähnlich wie im Befehlsmuster . Auf diese Weise können Clients auch ihre eigenen Anforderungen schreiben und neue Anforderungen können bei Bedarf von Plugins bereitgestellt werden (z. B. unter Verwendung der Einstiegspunkte von setuptools). Dies würde auch sicherstellen, dass dasRequest
Objekt niemals zu einer Gottklasse wird oder dass die API geändert wird, wenn neue Funktionen hinzugefügt werden.quelle