Begriff (oder "Muster"?) Für "etwas tun, wenn es nicht bereits erledigt ist" [geschlossen]

54

Klingt ziemlich einfach, ich weiß, aber ich habe kürzlich von einem Kollegen erfahren, dass eine aufgerufene Methode startHttpServerzu kompliziert ist, um sie zu verstehen, da sie den Server nur startet, wenn sie noch nicht ausgeführt wird. Ich stelle fest, dass ich Probleme bekomme, wenn ich antworte: "Ernsthaft? Ich mache das seit Jahrzehnten - es ist ein übliches Muster in der Programmierung." Öfter als ich zugeben möchte, kommt er mit einigen dokumentierten Beweisen zurück, die zeigen, dass die gesamte Programmier-Community hinter seiner Sichtweise steckt und ich mich am Ende verlegen fühle.

Frage : Gibt es ein dokumentiertes Entwurfsmuster hinter dem Konzept einer Methode, die keine Operation ist, wenn die erforderliche Aktion bereits wirksam ist? Oder, wenn kein Muster, hat es auch einen Namen? Und wenn nicht, gibt es einen Grund zu der Annahme, dass es zu kompliziert ist, eine Methode auf diese Weise zu schreiben?

John Calcote
quelle
6
klingt nach Caching - und es wird oft als kompliziert angesehen (siehe auch Wie erkläre ich $ {etwas} $ {jemandem}? )
gnat
8
Sie haben 3 verschiedene Antworten erhalten, aber die beste Antwort wäre: Wenden Sie alle an: Benennen Sie die ursprüngliche Funktion um, teilen Sie den Code in zwei Funktionen auf (wobei eine der beiden jetzt den Namen erhält startHttpServer), und ja, hier gilt der Begriff "idempotent" Gut.
Doc Brown
8
Welche Gegenbeweise liefert Ihr Kollege? Können Sie einen Link dazu bereitstellen?
Robert Harvey
5
Ich bin gespannt, woher Ihr Kollege seine Zitate bezieht. Zumindest um Stack Overflow und Software Engineering herum wäre diese Funktion höchstens schlecht benannt, hätte aber kein ungewöhnliches Verhalten. Denken Sie daran, dass dies nicht darauf zurückzuführen ist, dass jemand irgendwo etwas in ein Blog gestellt hat, das die gesamte Sicht der Programmierergemeinschaft repräsentiert. Sogar große Namen wie Martin Fowler sagen von Zeit zu Zeit etwas Seltsames. Wir sind alle nur Menschen.
T. Sar - Reinstate Monica
4
Ich denke, ein besserer Weg, um über "Tun Sie etwas, wenn es noch nicht getan ist" nachzudenken, wäre "Versetzen Sie das System in diesen Zustand". Wenn sich das System bereits in diesem Zustand befindet, würde die Methode natürlich nichts tun - was das erwartete Verhalten wäre.
T. Sar - Reinstate Monica

Antworten:

127

Wie NickWilliams bereits sagte : Das Konzept, das das OP beschreibt, heißt idempotent (Nomen Idempotency ). Dies ist in der Tat eine gängige Praxis, insbesondere bei APIs auf hoher Ebene.

ABER: Benennen Sie die Funktion um.

Anstatt es zu startHttpServernennen makeSureHttpServerIsRunningoder ensureHttpServerIsRunning.

Wenn eine Funktion aufgerufen wird startHttpServer, erwarten die Leser, dass sie einen HTTP-Server startet. Wenn ich zehnmal hintereinander anrufe, werden zehn Server ausgeführt. Ihre Funktion macht das die meiste Zeit nicht. Außerdem lässt der Name mit "start" darauf schließen, dass ich, wenn nur ein Server ausgeführt werden soll, nachverfolgen muss, ob die Funktion bereits aufgerufen wurde oder nicht.

Wenn eine Funktion aufgerufen wird makeSureHttpServerIsRunning, wird sie vermutlich die erforderlichen Schritte ausführen, um sicherzustellen, dass ein HTTP-Server ausgeführt wird. Dies geschieht höchstwahrscheinlich, indem überprüft wird, ob er bereits ausgeführt wird, und ansonsten gestartet wird. Ich gehe auch davon aus, dass die Funktion sicherstellt, dass der Server tatsächlich ausgeführt wird (das Starten eines Servers kann einige Zeit in Anspruch nehmen, in der er noch nicht vollständig ausgeführt wird).

gnasher729
quelle
83
Diese. Persönlich verwende ich stattdessen "Sicherstellen". Der Punkt ist, dass dies ein Benennungsschema werden sollte, das in Ihrem Team verstanden wird.
Euphoric
3
oder Start eine Ausnahme auslösen, wenn es bereits gestartet ist. like sqlconnection.open
Ewan
9
Ich benutze immer die Funktion "Stellen Sie sicher" für "tun, wenn nicht schon".
Kaz
3
Ein anderer Name, den ich oft sehe, ist getOrCreateSomething, das erst beim ersten Aufruf erstellt und dann einfach etwas
Fabich,
5
@aroth Sind Sie sicher, dass Sie nicht erwarten würden, dass es versucht, einen verfügbaren Port zu finden und ihn zurückzugeben? Oder einfach nur schlecht geschrieben sein? ;)
jpmc26
33

Benenne es um in EnsureServerRunning.

Völlig eindeutig und es ist klar, dass es sicherstellt, dass es ausgeführt wird (falls nicht), ohne einen Neustart zu implizieren, falls dies der Fall ist.

(Alternativ StartServerIfNotRunning:?)

Stilez
quelle
1
Die meisten Anrufer kümmern sich nur darum, dass der Server nach dem Anruf läuft. Wie das erreicht wird, ist ihnen egal.
gnasher729
29

Nicht wirklich ein Entwurfsmuster, aber ich würde Ihre Methode als idempotent bezeichnen . Dieser Begriff wird normalerweise für Remoteanrufe verwendet, aber die Beschreibung scheint dem zu entsprechen, was Sie tun.

Idempotente Methoden. Methoden können auch die Eigenschaft "idempotence" haben, dass (abgesehen von Fehler- oder Ablaufproblemen) die Nebenwirkungen von N> 0 identischen Anforderungen die gleichen sind wie bei einer einzelnen Anforderung. ( Von W3.org )

Der serverseitige Effekt ist hier, dass der http-Server gestartet wird, sobald die Methode aufgerufen wird. Ich sehe nichts falsches an einer Methode, die dies tut.

Wenn Sie ein Entwurfsmuster benötigen, können Sie Ihren httpServer als Singleton verfügbar machen, der beim Initialisieren gestartet wird.

Nick Williams
quelle
5
Die Funktion ist nicht idempotent, wenn der http-Server durch einmaliges Aufrufen und durch ein zweites Aufrufen nicht gestartet wird . Das ist buchstäblich das Gegenteil von Idempotenz. Es wäre idempotent, wenn bei jedem Aufruf ein neuer http-Server gestartet würde .
Polygnome
36
@Polygnome Wikipedia definiert Idempotenz als f (f (x)) = f (x), was allgemein mit Nicks Beschreibung übereinstimmt. Hier wäre die Funktionseingabe und -ausgabe der implizite Serverstatus, sodass der Status "Server läuft" ein fester Punkt für diese Funktion wäre. Vielleicht verstehe ich den Wikipedia-Artikel hier falsch. Können Sie auf andere Referenzen verweisen?
amon
16
@Polygnome: Diese Antwort ist richtig, idempotency bezieht sich auf Funktionen, für die es egal ist, ob sie einmal oder mehrmals aufgerufen werden, das Ergebnis bleibt immer gleich. Hier wird als Ergebnis ein http-Dienst ausgeführt, unabhängig davon, wie oft die Funktion aufgerufen wird.
Doc Brown
14
Die Verwirrung ergibt sich aus der Tatsache, dass die Idempotenz in ihrem Kern ein mathematisches Konzept ist, das auf Funktionen angewendet wird. Die Definition ist nett und einfach für diese und funktioniert auch gut für reine Funktionssprachen. Sobald Sie Nebenwirkungen einführen, wird es kompliziert - Sie müssen die Umgebung, in der Sie die Funktion ausführen, als (implizites) Argument und Ausgabe der Funktion betrachten. Wenn dieser Zustand nicht durch weitere Aufrufe der Funktion geändert wird, ist er idempotent, andernfalls nicht. In diesem Fall lautet der relevante Status "Wird der HTTP-Server ausgeführt" - die Funktion ist also idempotent.
Voo
10
@Polygnome Entschuldigung, Sie liegen falsch. Idempotent bedeutet, dass die Anfrage den gleichen Effekt hat, unabhängig davon, ob sie einmal oder mehrmals ausgeführt wird. Das Starten von N http-Servern, wenn N Duplikate der Anforderung empfangen werden, ist definitiv nicht idempotent.
Kaz
7

Als derjenige, der dieses Tool implementiert , startHttpServersollten Sie versuchen, es so einfach, reibungslos und nahtlos wie möglich zu gestalten ...

Die Logik der Funktion

Technisch gesehen , durch die Spaltung von startHttpServer‚s - Logik in 2 Funktionen und nannte sie separat , alle , was Sie tun , ist bewegend startHttpServer ‘ s Idempotenz in den Code stattdessen beide Funktionen aufrufen ... Außerdem , wenn Sie sowohl die Logik in eine dritte Funktion wickeln (was tut startHttpServerDies zwingt Sie dazu, nicht getrockneten Code zu schreiben und ihn exponentiell überall dort zu duplizieren, wo Sie ihn aufrufen müssten startHttpServer. Kurz gesagt, startHttpServer muss sich die isHttpServerRunningFunktion nennen.

Mein Punkt ist also:

  • isHttpServerRunningFunktion implementieren , da diese möglicherweise ohnehin eigenständig benötigt wird ...
  • Implementiere startHttpServeres isHttpServerRunning, um seine nächste Aktion entsprechend zu definieren ...

Sie können startHttpServerjedoch jeden Wert zurückgeben, den der Benutzer dieser Funktion benötigt, z. B .:

  • 0 => Fehler beim Starten des Servers
  • 1 => Server startet erfolgreich
  • 2 => Server wurde bereits gestartet

Die Benennung der Funktion

Was ist zuallererst das Hauptziel des Benutzers? So starten Sie den HTTP-Server , richtig?

Grundsätzlich ist es kein Problem, etwas zu starten, das bereits gestartet wurde, AKA 1*1=1. Zumindest für mich ensureHttpServerIsRunningscheint " " die Bezeichnung " " nicht kritisch erforderlich zu sein. Es interessiert mich mehr, wie lang, natürlich und einprägsam der Name der Funktion ist.

Wenn Sie nun wissen möchten, wie die Funktion unter der Haube im Detail funktioniert, gibt es die Dokumentation oder die Codequelle dafür. Ich meine, wie für jede andere Funktion aus Bibliothek / Framework / API / etc ...

Sie lernen die Funktion einmal, während Sie sie mehrmals schreiben ...

Ich würde mich auf jeden Fall daran halten, startHttpServerwas kürzer, einfacher und eindeutiger ist als ensureHttpServerIsRunning.

ClemC
quelle
1
Vor allem, weil die Methode einige Konfigurationsargumente wie das Stammverzeichnis, Sicherheitseinstellungen und möglicherweise eine Portnummer annehmen muss, bin ich hier zu 100% mit "start" zufrieden.
user949300
@ user949300: Der Aufrufer von "verifyHttpServerIsRunning" kümmert sich nicht um Konfiguration, Stammverzeichnis usw. Das ist die Aufgabe der Person, die es implementiert.
gnasher729
2
@ gnasher729 Das setzt voraus, dass der http-Server ein Singleton ist. Singletons sind böse. Und hier möglicherweise unangemessen. Man könnte leicht mehrere Server an mehreren Ports haben. Wenn es wirklich nur einen http-Server gibt, ist es besser, den Server bei der Programminitialisierung nur einmal zu starten.
user949300
2

Ich nehme an, dass Ihr Kollege gemeint hat, dass startHttpServerdas zu viel tut:

  • Überprüfung, ob der Server bereits läuft,
  • Starten Sie den Server bei Bedarf.

Das sind zwei nicht zusammenhängende Teile des Codes. Eine ähnliche Situation besteht beispielsweise, wenn eine Desktop-Anwendung sicherstellen soll, dass sie beim Start nicht bereits ausgeführt wird. Es wird einen Teil des Codes geben, der App-Instanzen behandelt (zum Beispiel unter Verwendung eines Mutex), und den Code, der die Anwendungsnachrichtenschleife startet.

Dies bedeutet, dass Sie nicht eine, sondern mindestens zwei Methoden haben müssen :

  • isHttpServerRunning: boolean
  • startHttpServer

Der Anwendungseinstiegspunkt ruft die erste Methode und die zweite auf, wenn der Rückgabewert lautet false. Jetzt macht jede Methode eins und eins und ist leicht zu verstehen.


¹ Wenn die Logik, die benötigt wird, um zu wissen, ob der Server bereits ausgeführt wird, zu komplex ist, ist möglicherweise eine weitere Aufteilung in mehrere Methoden erforderlich.

Arseni Mourzenko
quelle
21
Jetzt bewirkt ein anderer Aufruf der beiden Funktionen zwei Dinge, und wenn die Funktionen separat verfügbar gemacht werden, kann dies zu Race-Bedingungen führen. Kein freies Mittagessen.
Whatsisname
1
Wenn das für den Kollegen zu viel ist, viel Glück.
gnasher729
@ gnasher729: diese antwort widerspricht deiner nicht - im gegenteil, es könnte durchaus eine gute idee sein, die umbenennung der methode mit der aufteilung in zwei zu kombinieren. Und solange wir den tatsächlichen Code nicht sehen, wissen wir nicht, wie komplex der Code ist.
Doc Brown
10
Diese Antwort scheint Abstraktion und TROCKEN zu widersprechen. Was ist, wenn startHttpServermehr als eine Stelle im Code aufgerufen wird? Sollen überall mehrere ähnliche Zeilen eingefügt werden? Sollte dies mit allen Funktionen erfolgen? Bald wird Ihr Programm unendlich groß sein.
JacquesB
3
Ich denke, der erste Nebeneffekt dieses Ansatzes ist, dass der Beginn der startHttpServerMethode ungefähr so ​​aussehen wird if (isHttpServerRunning()){ return; }. Sie behaupten eine Geschäftsregel, dass "es nicht gültig ist, den HTTP-Server zu starten, wenn er bereits ausgeführt wird", aber dass dann die Verantwortung für die Durchsetzung dieser Regel bei einer anderen Person liegt. Ad-hoc und immer wieder an jedem Ort, an dem sie anrufen könnten startHttpServer.
Donnerstag,
2

Da Sie keine Sprache angeben, haben in JavaScript viele Bibliotheken eine "einmalige" Funktion, z . B. Unterstrich . Wenn Ihnen das bekannt ist, bezeichnen Sie es als "einmaliges" Muster und benennen Sie Ihre Methode möglicherweise um.

Ich selbst komme eher aus Java und denke an die Begriffe "Caching" oder "Lazy Evaluation". "Idempotent" ist technisch korrekt und eine gute Wahl. wenn Sie einen funktionaleren Hintergrund haben.

user949300
quelle
Es darf nicht "einmal" sein, wenn der Server gestoppt werden kann.
gnasher729
@ gnasher729. Ich könnte mir eine selten angewandte restartHttpServer()Methode vorstellen . Aber halt einfach an - was ist der Anwendungsfall dort? Sie mögen sporadische Verbindungsfehler? :-)
user949300
Es muss keinen Anwendungsfall geben, um den HTTP-Server anzuhalten, da er möglicherweise von einem externen Programm angehalten wurde - der HTTP-Server ist möglicherweise fehlerhaft und gestorben, ein Administrator hat ihn möglicherweise aus irgendeinem Grund manuell angehalten. etc.
Dave Sherohman
Dave, ein Absturz ist "außergewöhnlich" und sollte als solcher behandelt werden. Außerdem kann , da ein Absturz passieren kann jederzeit , würde nicht jede Zeile des Codes sein müssen ensureRunning()? :-) Was den Administrator angeht, wäre es unglaublich ärgerlich und falsch, wenn dieser andere Code ständig neu gestartet würde, während der Administrator versucht, etwas zu ändern oder zu reparieren. Lassen Sie den Administrator neu starten, nicht den Code.
user949300
-2

Ich würde es vorziehen startHttpServerIfNotIsRunning.

Auf diese Weise wird die Bedingung bereits im Methodennamen deutlich erwähnt. Ensureoder makeSurescheint mir ein bisschen vage, da es kein technischer Ausdruck ist. Es hört sich so an, als ob wir nicht genau wissen, was passieren wird.

Herr Derb
quelle
Ich nehme an, Sie sind kein Muttersprachler? Da gewährleisten hat die genaue Definition dessen , was wir gehen für. Trotzdem ist es fair, dass für Dokumentation und APIs keine Englischkenntnisse als Muttersprache erforderlich sind, um verständlich zu sein.
Voo
1
Danke ich genau nicht was das Ensurebedeutet. Ich mag dieses Wort für einen technischen Ausdruck immer noch nicht. Ensureist etwas menschliches. Ein System kann nichts sicherstellen, es wird nur das tun, was es tun soll.
Herr Derb
2
@Voo - Ich bin ein Muttersprachler und ich bin nicht sicher, was Sicherstellen bedeutet.
Jonathan Cast
Da alle, die hier posten, Zugang zum Internet haben, warum sollten sie nicht die Definition von "Sicherstellen" nachschlagen, wenn sie nicht sicher wären, was dies bedeutet? Ein bisschen Trivia ... viele Leute tauschen die Verwendung von "Versichern" und "Versichern" aus, ohne zu merken, dass die Verwendung eines dieser Wörter sie "rechtlich" verantwortlich machen könnte, während das andere Wort dies nicht tun würde.
Dunk
@jcast: Im Ernst?
gnasher729
-3

Was Ihr Kollege Ihnen hätte sagen sollen, ist, dass Sie kein Geschäft damit haben, diese Methode zu schreiben. Es wurde bereits viele Male geschrieben und ist besser, als Sie es wahrscheinlich schreiben werden. Beispiel: http://docs.ansible.com/ansible/latest/systemd_module.html https://docs.saltstack.com/de/latest/ref/states/all/salt.states.service.html

Aus architektonischer Sicht ist die Verwaltung eines Webservers mit einem willkürlichen Code ein wahrer Albtraum. Es sei denn, das Verwalten von Diensten ist ausschließlich die Aufgabe Ihres Codes. Aber ich vermute, Sie haben keine Monit (oder Kubernetes oder ...) geschrieben.

Andrew
quelle
2
Die Sprache ist Java und die Methode wurde entwickelt, um einen eingebetteten Grizzly-Server in einer eigenständigen Jersey-Anwendung zu starten. Ich kann die Richtigkeit Ihres Kommentars nicht beanstanden, da ich Ihnen nicht viel Kontext gegeben habe, aber Sie haben bei Ihrer Aussage viel vorausgesetzt. Es ist völlig in Ordnung, eine Methode zu haben, die in Jetty oder Grizzly ruft, um den Server zu starten.
John Calcote
1
Es gibt Millionen von Geschäftsanwendungen, die einen selbst gehosteten HTTP-Server enthalten, um eine API bereitzustellen. Und all diese benötigen einen sehr einfachen Code, der die von ihnen verwendete Bibliothek tatsächlich einrichtet und Informationen wie die zu verbindende Schnittstelle, den zu verwendenden Port, Sicherheitseinstellungen usw. bereitstellt. Eine startServerFunktion oder ähnliches zu haben, ist alles andere als ungewöhnlich . Das heißt nicht, dass Sie die kleinsten Details schreiben werden.
Voo