2017-12-28 9 views
0

Denken Sie an ein Beispiel wie eine Dienstleistung/Funktion, die einen Kunden auscheckt, es gibt viele Schritte: Server sollte zum Beispiel Inventar prüfen, Betrug überprüfen, die Kreditkarte belasten, Inventar abziehen, den Kauf schließen, per E-Mail versenden Kunde, benachrichtigen Sie das Backend/Handler/Warehouse und geben Sie dann die http-Anfrage zurück. In einer objektorientierten Sprache würde ich dieses Problem auf eine Art angehen, indem ich ein Dienstobjekt erstelle und diese Schritte synchron und in der Reihenfolge ausführen lasse, wenn etwas schief läuft (vielleicht sogar eine Zustandsmaschine).Wie können funktionale Sprachen wie Elixir große Ketten von Prozessen handhaben?

Wenn ich jedoch daran denke, wie ich dieses Problem in einer Sprache wie Elixir angehen würde, ist die einzige Lösung, die ich mir vorstellen kann, eine lange Kette von Rohren - die sich in der Elixir-Welt wie ein Anti-Muster anfühlt , besonders wenn Sie Verzweigungen in Betracht ziehen.

Der zweite Gedanke, den ich hatte, war, dass jeder Schritt seine eigene Funktion ist (die sich idiomatisch anfühlt), und es bedarf eines zusätzlichen Arguments über den Zustand des Kaufs. In diesem Fall könnte die validate_fraud Aktion dann charge_credit_card mit der {purchase, fraud_passed} Information anrufen und dann würde charge_credit_card dann die nächste in der Zeile aufrufen, wenn es fertig war. Dies würde jedoch bedeuten, dass jede Funktion ihren Platz in einer Kette kennen muss, die sich wiederum wie ein Geruch anfühlt (außerdem müsste jede Funktion eine Logik haben, um mit den verschiedenen ankommenden "Zuständen" umgehen zu können).

Was ist der idiomatische Weg in Elixir, mit einer Situation umzugehen, die die OO-Welt mit einem Serviceobjekt lösen würde?

+0

Eine Sache zu betrachten ist, dass ein "Service-Objekt" eine Menge Informationen in ein Stück Code koppelt. Das sollte dir auch nach einem Anti-Pattern riechen. –

+0

@OnorioCatenacci "Dienstobjekt" kann alle zehn Prüfer an verschiedene Module delegieren, was zu 12 Codezeilen führt, selbst wenn jeder "Delegierte" mit seiner eigenen Leitung versehen ist. – mudasobwa

+0

Ein schöner Punkt. Es klang nicht so, als ob er es so meinte. –

Antwort

2

Die funktionale Art der Sprache kann nichts zu Geschäftsregeln implizieren. Wenn die Kette so aussieht, wie Sie es beschrieben haben, gibt es keinen Wert (und tatsächlich keine Möglichkeit) Schritte asynchron zu machen, da validate_fraud vor inventory_check, noch nach charge_credit keinen Sinn ergibt.

In einem solchen Fall ist die Lösung sehr ähnlich dem OO-Service: man würde wahrscheinlich einen Prozess erzeugen (Task in diesem speziellen Fall) das wird Rohr alle Schritte:

task = Task.async(fn -> 
    check inventory() 
    |> validate fraud() 
    |> charge() 
    |> deduct_inventory() 
    |> close_purchase() 
    |> email_customer() 
    |> notify_handler() 
    |> return_http_request() 
end) 

Jetzt haben Aufgaben einen schönen Feature, könnte man überprüfen, ob es mit Task.yield/2 abgeschlossen ist, mit eigenen Timeout. Der Anrufercode ruft einfach Task.await/2 an, um den Anrufer zu blockieren, bis die Aufgabe beendet ist, oder besser, er wartet vielleicht 3 Sekunden mit Task.yield und antwortet entweder mit einem Ergebnis, wenn es fertig ist, oder mit einem "Versprechen" wenn Die Ausführung dauert länger.

Verwandte Themen