Angenommen, ich habe eine MediaPlayer-Klasse mit den Methoden play () und stop (). Was ist die beste Strategie für die Implementierung der Stop-Methode, wenn die Play-Methode noch nicht aufgerufen wurde? Ich sehe zwei Möglichkeiten: eine Ausnahme auslösen, weil sich der Player nicht in einem geeigneten Zustand befindet, oder Aufrufe der Stopp-Methode stillschweigend ignorieren.
Was sollte die allgemeine Regel sein, wenn eine Methode in bestimmten Situationen nicht aufgerufen werden soll, ihre Ausführung jedoch dem Programm im Allgemeinen keinen Schaden zufügt?
exceptions
methods
state
x2bool
quelle
quelle
Antworten:
Es gibt keine Regel. Es liegt ganz bei Ihnen, wie Sie Ihre API "fühlen" lassen möchten.
Persönlich in einem Musik - Player, ich glaube , ein Übergang von dem Zustand
Stopped
zuStopped
mittels des VerfahrensStop()
ist ein absolut gültiger Zustandsübergang. Es ist nicht sehr aussagekräftig, aber es ist gültig. In diesem Sinne erscheint das Auslösen einer Ausnahme pedantisch und unfair. Dadurch würde sich die API wie das soziale Äquivalent eines Gesprächs mit dem nervigen Kind im Schulbus anfühlen. Sie stecken in der nervigen Situation fest, aber Sie können damit umgehen.Ein "geselliger" Ansatz ist es, zu erkennen, dass der Übergang im schlimmsten Fall harmlos ist, und freundlich zu Ihren konsumierenden Entwicklern zu sein, indem Sie ihn zulassen.
Wenn es nach mir ginge, würde ich die Ausnahme auslassen.
quelle
MediaPlayer.stop()
Methode, dieIllegalStateException
stundenlanges Debuggen von Code ersetzte und sich fragte, warum zum Teufel "nichts passiert" (dh "es funktioniert einfach nicht").StopOrThrow()
klingt schrecklich Wenn Sie diese Gasse entlang gehen, warum nicht das Standardmuster von verwendenTryStop()
? Wenn das Verhalten einer API unklar ist (wie die meisten von ihnen), wird von Entwicklern nicht erwartet, dass sie einfach raten oder experimentieren, sondern dass sie sich die Dokumentation ansehen. Deshalb mag ich MSDN so sehr.Es gibt zwei verschiedene Arten von Aktionen, die Sie ausführen möchten:
Testen Sie gleichzeitig, ob sich etwas in einem Zustand befindet, und ändern Sie ihn in einen anderen.
Legen Sie einen bestimmten Status fest, ohne den vorherigen Status zu berücksichtigen.
Einige Kontexte erfordern eine Aktion und einige erfordern die andere. Wenn ein Mediaplayer, der das Ende des Inhalts erreicht, in einem "Wiedergabe" -Zustand verbleibt, aber die Position am Ende eingefroren ist, dann eine Methode, die gleichzeitig bestätigt, dass sich der Player in einem Wiedergabezustand befand, während er auf "Stopp" gesetzt wurde. state kann nützlich sein, wenn Code sicherstellen möchte, dass die Wiedergabe vor der Stoppanforderung nicht fehlgeschlagen ist (was wichtig sein kann, wenn z. B. der wiedergegebene Inhalt aufgezeichnet wird, um das Medium von einem Format in ein anderes zu konvertieren). . In den meisten Fällen kommt es jedoch darauf an, dass sich der Media Player nach dem Vorgang im erwarteten Zustand befindet (dh angehalten wurde).
Wenn sich ein Mediaplayer am Ende des Mediums automatisch selbst stoppt, ist eine Funktion, die angibt, dass der Player ausgeführt wird, wahrscheinlich eher ärgerlich als hilfreich. In einigen Fällen kann es jedoch hilfreich sein, zu wissen, ob der Player zum Zeitpunkt des Stopps ausgeführt wurde nützlich sein. Möglicherweise besteht die beste Lösung für beide Anforderungen darin, dass die Funktion einen Wert zurückgibt, der den vorherigen Status des Spielers angibt. Code, der sich um diesen Status kümmert, kann den Rückgabewert untersuchen. Code, der sich nicht darum kümmert, könnte ihn einfach ignorieren.
quelle
bool TryStop()
und einvoid Stop()
Codebeispiel würden dies zu einer wirklich ausgezeichneten Antwort machen.TryStop
würde bedeuten, dass eine Ausnahme nicht geworfen werden sollte, wenn der Spieler nicht in einen gestoppten Zustand gezwungen werden kann. Der Versuch, den Spieler zu stoppen, wenn er bereits gestoppt ist, ist keine Ausnahmebedingung, aber eine Bedingung, an der ein Anrufer interessiert sein könnte.isPlaying()
.Es gibt keine allgemeine Regel. In diesem speziellen Fall möchte der Benutzer Ihrer API den Player daran hindern, die Medien abzuspielen. Wenn der Player die Medien nicht abspielt, unternimmt der
MediaPlayer.stop()
möglicherweise nichts und das Ziel des Methodenaufrufers wird weiterhin erreicht - die Medien werden nicht abgespielt.Beim Auslösen einer Ausnahme muss der Benutzer der API entweder prüfen, ob der Player gerade spielt, oder die Ausnahme abfangen und behandeln. Dies würde die API zu einer mühsameren Aufgabe machen.
quelle
Mit Ausnahmen soll nicht signalisiert werden, dass etwas Schlimmes passiert ist. es soll das signalisieren
Wenn der Anrufer versucht,
Stop
eine Verbindung herzustellenMediaPlayer
, die sich bereits in einem angehaltenen Zustand befindet, ist dies kein Problem, dasMediaPlayer
nicht gelöst werden kann (es kann einfach nichts tun). Wenn dies fortgesetzt wird, führt dies nicht zu Schäden oder Daten Korruption, da sie einfach durch Nichtstun erfolgreich sein kann. Und es ist nicht wirklich ein Problem, dass es vernünftiger ist, zu erwarten, dass der Anrufer in der Lage ist, eine Lösung zu finden als dieMediaPlayer
.Aus diesem Grund sollten Sie in dieser Situation keine Ausnahme auslösen.
quelle
Schau es dir so an:
Wenn der Client Stop () aufruft, während der Player nicht spielt, ist Stop () automatisch erfolgreich, da sich der Player derzeit im gestoppten Zustand befindet.
quelle
In der Regel tun Sie, was der Vertrag Ihrer Methode erfordert.
Ich sehe mehrere Möglichkeiten, sinnvolle Verträge für eine solche
stop
Methode zu definieren . Es kann durchaus sinnvoll sein, dass diestop
Methode absolut nichts tut, wenn der Spieler nicht spielt. In diesem Fall definieren Sie Ihre API anhand ihrer Ziele. Sie möchten in denstopped
Zustand übergehen , und diestop
Methode tut dies, indem Sie einfachstopped
fehlerfrei aus dem Zustand in sich selbst übergehen .Gibt es einen Grund, warum Sie eine Ausnahme auslösen möchten ? Wenn der Benutzercode am Ende so aussehen wird:
dann hat diese Ausnahme keinen Vorteil. Die Frage ist also, ob es dem Benutzer wichtig ist, dass der Player bereits gestoppt wurde. Hat er noch eine Frage?
Der Spieler kann beobachtbar sein und Beobachter bereits über bestimmte Dinge benachrichtigen - z. B. Beobachter benachrichtigen, wenn er von
playing
nachstopping
nach wechseltstopped
. In diesem Fall ist es nicht erforderlich, dass die Methode eine Ausnahme auslöst. Der Aufrufstop
wird einfach nichts tun , wenn der Spieler bereits gestoppt ist, und nannte es , wenn es nicht benachrichtigt die Beobachter über den Übergang gestoppt.Alles in allem hängt es davon ab, wo Ihre Logik landen wird. Aber ich denke, es gibt bessere Entwurfswahlen als das Werfen einer Ausnahme.
Sie sollten eine Ausnahme auslösen, wenn der Anrufer eingreifen muss. In diesem Fall muss er nicht, Sie können den Player einfach so lassen, wie er ist, und es funktioniert weiter.
quelle
Es gibt eine einfache allgemeine Strategie, mit der Sie diese Entscheidung treffen können.
Überlegen Sie, wie die Ausnahme behandelt werden soll, wenn sie ausgelöst wird.
Stellen Sie sich also Ihren Musik-Player vor, der Benutzer klickt auf Stopp und dann erneut auf Stopp. Möchten Sie in diesem Szenario eine Fehlermeldung anzeigen? Ich habe noch keinen Spieler gesehen, der das tut. Möchten Sie, dass sich das Anwendungsverhalten von einem einfachen Klicken auf Stopp unterscheidet (z. B. das Protokollieren des Ereignisses oder das Senden eines Fehlerberichts im Hintergrund)? Wahrscheinlich nicht.
Das heißt, die Ausnahme müsste irgendwo gefangen und geschluckt werden. Und das bedeutet, dass Sie besser dran sind, die Ausnahme gar nicht erst auszulösen, weil Sie keine andere Aktion ausführen möchten .
Dies gilt nicht nur für Ausnahmen, sondern für jede Art von Verzweigung: Wenn es keine feststellbaren Verhaltensunterschiede geben soll, ist keine Verzweigung erforderlich.
Das heißt, es schadet nicht, Ihre
stop()
Methode so zu definieren , dass sie einenboolean
oder einen Aufzählungswert zurückgibt, der angibt, ob das Stoppen "erfolgreich" war. Sie werden es wahrscheinlich nie verwenden, aber ein Rückgabewert kann einfacher und natürlicher ignoriert werden als eine Ausnahme.quelle
So macht es Android: MediaPlayer
Kurz gesagt,
stop
wennstart
nicht angerufen wurde, ist das kein Problem, das System bleibt im gestoppten Zustand. Wenn jedoch ein Spieler angerufen wird, der nicht einmal weiß, was er spielt, wird eine Ausnahme ausgelöst, da es keinen guten Grund für einen Anruf gibt halte dort an.quelle
Ich sehe, was ein kleiner Widerspruch in Ihrer Aussage sein könnte, der Ihnen die Antwort klar machen könnte. Warum soll die Methode nicht aufgerufen werden und dennoch schadet ihre Ausführung nichts ?
Warum soll die Methode "nicht aufgerufen werden"? Das scheint eine selbst auferlegte Einschränkung zu sein. Wenn Ihre API keinen "Schaden" oder Einfluss hat, gibt es keine Ausnahme. Wenn die Methode wirklich nicht aufgerufen werden sollte, weil sie möglicherweise ungültige oder unvorhersehbare Zustände erzeugt, sollte eine Ausnahme ausgelöst werden.
Wenn ich zum Beispiel zu einem DVD-Player gelaufen bin und auf "Stopp" geklickt habe, bevor ich auf "Start" geklickt habe und er abgestürzt ist, würde das für mich keinen Sinn ergeben. Es sollte nur dort sitzen oder im schlimmsten Fall "Stop" auf dem Bildschirm bestätigen. Ein Fehler wäre ärgerlich und in keiner Weise hilfreich.
Kann ich jedoch einen Alarmcode eingeben, um das Gerät auszuschalten, wenn es bereits ausgeschaltet ist (Aufrufen der Methode)? Wenn ja, dann werfen Sie einen Fehler, obwohl technisch "es keinen Schaden angerichtet hat", weil es später Probleme geben kann. Ich würde es gerne wissen, auch wenn es nicht in diesen Zustand ging. Nur die Möglichkeit ist genug. Das wäre ein
IllegalStateException
.Wenn in Ihrem Fall Ihr Zustandsautomat den ungültigen / unnötigen Aufruf verarbeiten kann, ignorieren Sie ihn, andernfalls wird ein Fehler ausgegeben.
BEARBEITEN:
Beachten Sie, dass ein Compiler keinen Fehler auslöst, wenn Sie eine Variable zweimal festlegen, ohne den Wert zu überprüfen. Es hindert Sie auch nicht daran, eine Variable neu zu initialisieren. Es gibt viele Aktionen, die aus einer Perspektive als "Fehler" betrachtet werden könnten, aber nur "ineffizient", "unnötig", "sinnlos" usw. Ich habe versucht, diese Perspektive in meiner Antwort anzugeben - dies wird im Allgemeinen nicht berücksichtigt Ein "Bug", weil er nichts Unerwartetes zur Folge hat. Sie sollten Ihr Problem wahrscheinlich ähnlich angehen.
quelle
Was genau tun
MediaPlayer.play()
undMediaPlayer.stop()
tun? - Sind sie Ereignis- Listener für Benutzereingaben oder sind sie tatsächlich Methoden, die eine Art Medienstrom auf dem System starten? Wenn alles, was sie sind, Listener für Benutzereingaben sind, ist es für sie völlig vernünftig, nichts zu tun (obwohl es eine gute Idee wäre, sie zumindest irgendwo zu protokollieren). Wenn sie jedoch die Auswirkungen auf Modell UI steuert, dann könnten sie einen werfen ,IllegalStateException
weil der Spieler eine Art haben sollteisPlaying=true
oderisPlaying=false
Zustand (es muss nicht eine tatsächliche Boolesche Flag wie hier geschrieben sein), und so , wenn Sie anrufen ,MediaPlayer.stop()
wennisPlaying=false
die Diese Methode kannMediaPlayer
das Objekt nicht "stoppen", da es sich nicht in dem zu stoppenden Zustand befindet - siehe Beschreibung desjava.lang.IllegalStateException
Klasse:Warum es gut sein kann, Ausnahmen zu werfen
Viele Leute scheinen zu glauben, dass Ausnahmen generell schlecht sind, da sie niemals geworfen werden sollten (vgl. Joel on Software ). Nehmen wir jedoch an, Sie haben einen
MediaPlayerGUI.notifyPlayButton()
which-AufrufMediaPlayer.play()
undMediaPlayer.play()
rufen eine Reihe anderen Codes auf, der irgendwo in der Folge mit z. B. PulseAudio zusammenarbeitet , aber ich (der Entwickler) weiß nicht, wo, weil ich nicht den gesamten Code geschrieben habe.Dann, eines Tages, während ich am Code arbeite, klicke ich auf "Abspielen" und nichts passiert. Wenn
MediaPlayerGUIController.notifyPlayButton()
etwas protokolliert wird, kann ich zumindest in den Protokollen nachsehen, ob der Tastenklick tatsächlich registriert wurde ... aber warum wird er nicht abgespielt? Angenommen, mit den PulseAudio-Wrappern stimmt tatsächlich etwas nichtMediaPlayer.play()
. Ich klicke erneut auf den "Play" -Button und diesmal erhalte ich eineIllegalStateException
:Wenn ich mir diesen Stack-Trace ansehe, kann ich
MediaPlayer.play()
beim Debuggen alles ignorieren und herausfinden, warum z. B. keine Nachricht an PulseAudio gesendet wird, um einen Audiostream zu starten.Auf der anderen Seite, wenn Sie keine Ausnahme machen, habe ich nichts anderes zu tun als: "Das blöde Programm spielt keine Musik ab"; Obwohl es nicht so schlimm , wie explizite Fehler versteckt , wie eigentlich eine Ausnahme beim Schlucken , das war in der Tat geworfen , ich ist immer noch potentiell andere Zeit zu verschwenden , wenn etwas schief gehen tut ... so schön, und eine Ausnahme auf sie werfen.
quelle
Ich bevorzuge es, die API so zu gestalten, dass es für den Verbraucher schwieriger oder unmöglich wird, Fehler zu machen. Beispielsweise könnten Sie anstelle von
MediaPlayer.play()
und angebenMediaPlayer.stop()
,MediaPlayer.playToggle()
welche Option zwischen "Angehalten" und "Spielen" wechselt. Auf diese Weise ist die Methode immer aufrufsicher - es besteht kein Risiko, in einen illegalen Zustand zu gelangen.Dies ist natürlich nicht immer möglich oder einfach zu bewerkstelligen. Das Beispiel, das Sie angegeben haben, ist vergleichbar mit dem Versuch, ein Element zu entfernen, das bereits aus der Liste entfernt wurde. Sie können entweder sein
if (list.contains(x)) { list.remove(x) }
Sie nur schreiben müssenlist.remove(x)
). Es kann aber auch Fehler verbergen.Wenn das Aufrufen,
MediaPlayer.stop()
wenn es bereits gestoppt ist, in Ihrer Anwendung keinen Schaden anrichtet, dann würde ich es unbemerkt laufen lassen, weil es den Code vereinfacht und ich ein Faible für idempotente Methoden habe. Aber wenn Sie absolut sicher sind, dassMediaPlayer.stop()
dies unter diesen Bedingungen niemals aufgerufen werden würde , würde ich einen Fehler auslösen, da der Code möglicherweise irgendwo anders fehlerhaft ist und die Ausnahme dabei helfen würde, ihn aufzuspüren.quelle
playToggle()
dass die Bedienung einfacher ist: Um den gewünschten Effekt zu erzielen (Spieler spielen oder nicht spielen), müssten Sie den aktuellen Status kennen. Wenn Sie also eine Benutzereingabe erhalten, in der Sie aufgefordert werden, den Player zu stoppen, müssen Sie zunächst nachfragen, ob der Player gerade spielt, und dann den Status abhängig von der Antwort umschalten. Das ist viel umständlicher, als nur einestop()
Methode aufzurufen und damit fertig zu sein.playToggle
wäre eine Verwendung sehr umständlich. Aber wenn der Benutzer stattdessen auch eine Umschalttaste erhalten würde,playToggle
wäre dies einfacher zu bedienen als das Abspielen / Stoppen.