Was sind einige Beispiele aus der Praxis die Schlüsselrolle von Behauptungen zu verstehen?
Antworten
Zu viele Anzeigen?Assert ist よほど nützlich bei der Entwicklung. Sie verwenden es, wenn etwas gerade kann nicht passieren, wenn Ihr Code korrekt funktioniert. Es ist einfach zu verwenden und kann für immer im Code bleiben, weil es im wirklichen Leben ausgeschaltet wird.
Wenn die Möglichkeit besteht, dass der Zustand im wirklichen Leben auftritt, müssen Sie damit umgehen.
Ich liebe es, aber ich weiß nicht, wie ich es in Eclipse/Android/ADT einschalten kann. Es scheint sogar beim Debuggen ausgeschaltet zu sein. (Es gibt einen Thread zu diesem Thema, aber er bezieht sich auf die "Java vm", die nicht in der ADT Run Configuration erscheint).
Hier ist eine Assertion, die ich in einem Server für ein Hibernate/SQL-Projekt geschrieben habe. Eine Entity Bean hatte zwei effektiv boolesche Eigenschaften, isActive und isDefault genannt. Jede dieser Eigenschaften konnte den Wert "Y" oder "N" oder null haben, was als "N" behandelt wurde. Wir wollen sicherstellen, dass der Browser-Client auf diese drei Werte beschränkt ist. Daher habe ich in meinen Settern für diese beiden Eigenschaften diese Behauptung hinzugefügt:
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
Beachten Sie das Folgende.
-
Diese Behauptung gilt nur für die Entwicklungsphase. Wenn der Kunde einen falschen Wert sendet, werden wir das früh erkennen und beheben, lange bevor wir die Produktion erreichen. Assertions sind für Fehler gedacht, die Sie frühzeitig erkennen können.
-
Diese Behauptung ist langsam und ineffizient. Das ist in Ordnung. Behauptungen können ruhig langsam sein. Es ist uns egal, denn sie sind reine Entwicklungswerkzeuge. Dies wird den Produktionscode nicht verlangsamen, da Assertions deaktiviert werden. (In diesem Punkt gibt es einige Meinungsverschiedenheiten, auf die ich später eingehen werde.) Dies führt zu meinem nächsten Punkt.
-
Diese Behauptung hat keine Nebenwirkungen. Ich hätte meinen Wert mit einem unveränderlichen statischen endgültigen Set testen können, aber dieses Set wäre in der Produktion geblieben, wo es nie verwendet werden würde.
-
Diese Behauptung dient dazu, den ordnungsgemäßen Betrieb des Clients zu überprüfen. Wenn wir die Produktion erreichen, werden wir sicher sein, dass der Client ordnungsgemäß funktioniert, so dass wir die Assertion sicher deaktivieren können.
-
Manche Leute fragen das: Wenn die Assertion in der Produktion nicht benötigt wird, warum nimmt man sie nicht einfach heraus, wenn man fertig ist? Weil man sie immer noch braucht, wenn man mit der Arbeit an der nächsten Version beginnt.
Einige Leute haben argumentiert, dass man niemals Assertions verwenden sollte, weil man nie sicher sein kann, dass alle Fehler beseitigt sind, so dass man sie auch in der Produktion beibehalten muss. Es macht also keinen Sinn, die Assert-Anweisung zu verwenden, da der einzige Vorteil von Asserts darin besteht, dass man sie abschalten kann. Nach dieser Auffassung sollte man also (fast) nie Asserts verwenden. Ich bin anderer Meinung. Es ist sicherlich richtig, dass man keine Assert-Anweisung verwenden sollte, wenn ein Test in die Produktion gehört. Aber dieser Test tut es no gehören in die Produktion. Hier geht es darum, einen Fehler zu finden, der wahrscheinlich nie in die Produktion gelangen wird, so dass er sicher ausgeschaltet werden kann, wenn Sie fertig sind.
Übrigens, ich hätte es auch so schreiben können:
assert value == null || value.equals("Y") || value.equals("N") : value;
Bei nur drei Werten ist das in Ordnung, aber wenn die Anzahl der möglichen Werte größer wird, ist die HashSet-Version praktischer. Ich habe die HashSet-Version gewählt, um meinen Standpunkt zur Effizienz zu verdeutlichen.
Assertions sind Prüfungen, die abgeschaltet werden können. Sie werden selten benutzt. Warum eigentlich?
- Sie dürfen nicht zur Überprüfung der Argumente öffentlicher Methoden verwendet werden, da Sie keine Kontrolle über sie haben.
- Sie sollten nicht für einfache Kontrollen verwendet werden wie
result != null
da solche Kontrollen sehr schnell sind und es kaum etwas zu sparen gibt.
Was bleibt dann noch übrig? Teuer prüft auf tatsächliche Bedingungen erwartet um wahr zu sein. Ein gutes Beispiel sind die Invarianten einer Datenstruktur wie RB-Baum. In der Tat, in ConcurrentHashMap
von JDK8 gibt es ein paar solche sinnvollen Asserts für die TreeNodes
.
- Man sollte sie in der Produktion wirklich nicht einschalten, da sie leicht die Laufzeit dominieren könnten.
- Möglicherweise möchten Sie sie während der Tests ein- oder ausschalten.
- Sie sollten sie auf jeden Fall einschalten, wenn Sie an dem Code arbeiten.
Manchmal ist die Prüfung nicht wirklich teuer, aber gleichzeitig ist man ziemlich sicher, dass sie bestanden wird. In meinem Code gibt es z.B.,
assert Sets.newHashSet(userIds).size() == userIds.size();
Ich bin mir ziemlich sicher, dass die Liste, die ich gerade erstellt habe, eindeutige Elemente enthält, aber ich wollte sie dokumentieren und doppelt überprüfen.
Zur Erinnerung (und das gilt für viele Sprachen, nicht nur für Java):
"Assert" wird von Softwareentwicklern in erster Linie als Hilfsmittel bei der Fehlersuche verwendet. Assert-Meldungen sollten niemals erscheinen. Viele Sprachen bieten zur Kompilierzeit eine Option an, die dazu führt, dass alle "Asserts" bei der Erzeugung von "Produktions"-Code ignoriert werden.
"Ausnahmen" sind ein praktischer Weg, um alle Arten von Fehlerzuständen zu behandeln, unabhängig davon, ob es sich um logische Fehler handelt oder nicht, denn wenn man auf einen Fehlerzustand stößt, so dass man nicht weitermachen kann, kann man sie einfach "in die Luft werfen", egal wo man sich befindet, in der Erwartung, dass jemand anderes da draußen bereit ist, sie "aufzufangen". Die Kontrolle wird in einem Schritt übertragen, direkt vom Code, der die Ausnahme ausgelöst hat, zum Fänger. (Und der Fänger kann die komplette Rückverfolgung der Anrufe sehen, die stattgefunden haben).
Außerdem müssen die Aufrufer dieses Unterprogramms nicht prüfen, ob das Unterprogramm erfolgreich war: "Wenn wir jetzt hier sind, muss es erfolgreich gewesen sein, denn sonst hätte es eine Ausnahme ausgelöst und wir wären jetzt nicht hier!" Diese einfache Strategie macht das Entwerfen von Code und das Debugging viel, viel einfacher.
Ausnahmen erlauben es fatalen Fehlern, das zu sein, was sie sind: "Ausnahmen von der Regel". Und damit sie von einem Code-Pfad behandelt werden können, der ebenfalls "eine Ausnahme von der Regel" ist ... "Fliegender Ball!"
Hier ein weiteres Beispiel. Ich habe eine Methode geschrieben, die den Median der Werte in zwei sortierten Arrays findet. Die Methode geht davon aus, dass die Arrays bereits sortiert sind. Aus Leistungsgründen sollte sie die Arrays NICHT zuerst sortieren oder sogar überprüfen, ob sie sortiert sind. Es ist jedoch ein schwerwiegender Fehler, diese Methode mit unsortierten Daten aufzurufen, und wir wollen, dass solche Fehler in der Entwicklungsphase frühzeitig erkannt werden. Hier ist also, wie ich diese scheinbar widersprüchlichen Ziele behandelt habe:
public static int medianOf(int[] a, int[] b) {
assert assertionOnlyIsSorted(a); // Assertion is order n
assert assertionOnlyIsSorted(b);
... // rest of implementation goes here. Algorithm is order log(n)
}
public static boolean assertionOnlyIsSorted(int[] array) {
for (int i=1; i<array.length; ++i) {
if (array[i] < array[i-1]) {
return false;
}
return true;
}
}
Auf diese Weise wird der langsame Test nur während der Entwicklungsphase durchgeführt, in der die Geschwindigkeit weniger wichtig ist als das Auffinden von Fehlern. Sie wollen die medianOf()
Methode, um log(n)-Leistung zu haben, aber der "is sorted"-Test hat die Reihenfolge n. Also habe ich ihn in eine Assertion eingebaut, um seine Verwendung auf die Entwicklungsphase zu beschränken, und ich gebe ihm einen Namen, der deutlich macht, dass er nicht für die Produktion geeignet ist.
Auf diese Weise habe ich das Beste aus beiden Welten. Bei der Entwicklung weiß ich, dass jede Methode, die dies falsch aufruft, abgefangen und korrigiert wird. Und ich weiß, dass der langsame Test, um dies zu tun, die Leistung in der Produktion nicht beeinträchtigen wird. (Dies ist auch ein gutes Beispiel dafür, warum man Assertions in der Produktion deaktivieren, aber in der Entwicklung einschalten sollte).