Ist Assert Sinnvoll
 
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern

Von ZeigerAufAusreichendPlatzAlsCeeIdiom.
assert() bietet in C und C++ einen Mechanismus zur Dokumentation von wahren Aussagen über den Programmzustand an einer bestimmten Stelle, den gegenüber einem reinen Kommentar den Vorteil hat, dass er - abschaltbar - zur Laufzeit überprüft werden kann. Ist NDEBUG abgeschaltet, führt eine verletzte assert()-Bedingung zum unbedingten Programmabbruch. Ist NDEBUG eingeschaltet, wird die angenommene Bedingung nicht einmal ausgewertet.

Die Verwendung von assert wird von der Mehrheit der Programmierer positiv bewertet. Ist das richtig? Was spricht für bzw. gegen seine Verwendung?

Pro:

Contra:
Erfahrungsbericht

Soweit die Theorie ... ich wollte heute voller Begeisterung endlich mal das neue Java assert einsetzen ... aber:

Ich glaub, inzwischen ist meine anfängliche Begeisterung doch arg geschrumpft. -- mj
Was haben Aspekte mit assertions zu tun ? Kannst du ein Beispiel für einen solchen Aspekt geben ? -- mj

Man kann Aspekte implementieren, die für die Überwachung der Zusicherung bzw. des Kontraktes zuständig sind. Diese Aspekte können beim Kompilieren der Software je nach Bedarf selektiv entweder aktiviert oder deaktiviert werden. Hier ein Beispiel aus dem Programmierhanbuch von AspectJ. Dieser Aspekt übeprüft die Eingangsparameter der Methoden, die die Koordinaten eines Puktes ändern:

aspectPointBoundsChecking {
    pointcut setX(int x): (call(void FigureElement.setXY(int, int)) ||
                           call(void Point.setX(int))) && args(x, ..);
    pointcut setY(int y): (call(void FigureElement.setXY(int, int)) ||
                           call(void Point.setY(int))) && args(y, ..);
    before(int x): setX(x) {
        if ( x < MIN_X || x > MAX_X )
            throw new IllegalArgumentException("x is out of bounds.");
    }
    before(int y): setY(y) {
        if ( y < MIN_Y || y > MAX_Y )
            throw new IllegalArgumentException("y is out of bounds.");
    }
}

Hier etwas komplizierter Kontrakt: Nur definierte Factorymethoden, die hier mit make anfangen, dürfen Figuren registrieren. Aufrufe der Methode register sind aus anderen Methoden nicht erlaubt:

static aspect RegistrationProtection {
    pointcut register(): call(void Registry.register(FigureElement));
    pointcut canRegister(): withincode(static * FigureElement.make*(..));
    before(): register() && !canRegister() {
        throw new IllegalAccessException("Illegal call " + thisJoinPoint);
    }
}
--gR

Okay, mit Aspecten kann man Assertions nachbauen. Das macht Assertions aber nicht besser (und Aspecte an dieser Stelle nicht sinnvoller).
"Assert" ist zumindest ein Keyword, das ein Compiler ode ein Doku-Extractor in Zukunft irgendwann vielleicht mal vernünftig auswerten kann.
Im übrigen finde ich es bei Assertions schlecht, dass sie abschaltbar sind. Aspecte, die dieses Abschalten simulieren, sind da also kein Argument. -- mj

Mit Aspekten kann man viel mehr als Assertions nachbauen, aber das sollte hier nicht das Thema sein. Was hier allerdings Aspekte sinnvoller macht, ist die Möglichkeit, sie selektiv abzuschalten - d.h. ich kann die Überprüfungen abschalten, die nur dazu dienen, meine eigene Fehler zu entdecken. (Diese Übrprüfungen können aufwendig sein, und es ist dann sinnvoll, sie abzuschalten) Überprüfungen der Einhaltung der Kontrakte mit der "Außenwelt" können aktiv bleiben.

Man sollte diese zwei Arten der Überprüfung strikt auseinander halten.

Eine Assertion sollte an Stellen verwendet werden, auf denen eine Bedingung als wahr bewiesen gilt. Sie sollte nur dazu dienen, eigene Programmierfehler zu entdecken. Da die Bedingung als bewiesen Gilt, kann man die Assertion in der Releaseversion getrost abschalten, ähnlich wie ein Gerüst nach der Fertigstellung eines Gebäude abgebaut werden kann. (Beispiel: Überpüfung, das nach dem Aufruf der Methode sort() eine Liste sortiert ist)

Ein Kontrakt beschreibt eine Bedingung die von einem "Partner" verlangt wird, und, da wir nicht die Kontrolle über diesen Partner haben, nicht bewiesen werden kann. Es ist oft sinnvoll die Überprüfung eines Kontraktes auch in der Releaseversion aktiv zu lassen. Deren Abschaltung kann man als Optimierung in Betracht ziehen. Aus diesem Grund sollte man für Kontrakte mit der Aussenwelt nicht Assertions verwenden.

Aspekte sind eine Art die Assertions und Kontraktüberpüfungen zu implementieren. Nicht nur die Auswahlmöglichkeit, welche an und abgeschaltet werden, bringt Vorteile. Sie haben in manchen Situationen den Vorteil, dann man den Kontrakt sehr kompakt beschreiben kann, im Gegensatz zu Assertions/Verifies usw., die in diesen Situationen an vielen Stellen verstreut werden müssten. Als Beispiel soll die obengenannte RegistrationProtection? dienen.

Übringens, sollte man statt Assertion nicht lieber Zusicherung sagen? Anglizismen sind doch so lame :-) --gR


Wenn Du es nicht abschalten können willst, dann nimm nicht assert() und nichts, was den assert()-Mechanismus von C oder C++ nachzubilden versucht. assert() ist ein Hilfsmittel, um Dokumentation abschaltbar zu Code werden zu lassen, der die Richtigkeit der dokumentierten Behauptung überprüft. Das ist die Idee hinter assert() in C, C++ und hinter dem assert-Mechanismus in Java. Wenn Du Eingabeparameter von Funktionen mit assert() überprüfen willst, dann musst Du alle moeglichen Aufrufer dieser Funktion unter Kontrolle haben. Wenn Die Prüfung Teil deiner Programmlogik ist, dann bilde einen eigene Mechanismus zur Verifikation von Bedingungen, aber setze in C oder C++ nicht assert() ein und wirf nicht mit Error-Exemplaren um dich. -- kw

Falls dich meine Error-Exemplare nicht interessieren, ignoriere sie bitte einfach ;-). Ich habe ganz einfach Bedarf nach genauerer Eingabeparameter Spezifikation und deren (automatischen) Dokumentation. Es ist schon richtig, dass ich da wohl auf die Fälle 1. & 2. aus AssertionVermeidung gestossen bin. Als alter Optimierer wünsche ich mir einfach nicht für alles Objekte erzeugen zu müssen, sondern möchte manchmal auch native Datentypen genauer spez. können.
Dazu braucht's dann nicht abschaltbare Assertions, die evtl. unterschiedliche Exceptions werfen können (Dokumentation und Eingebeeinschränkung), oder ich lasse mit einem Tool statisch die Fehlerfälle berechnen und mache dann Testcases daraus (erweiterte statische Typruefung). -- mj

Du brauchst einen Mechanismus, mit dem du Vorbedingungen so spezifizieren kannst, dass sie immer überprüft werden. Dieser Mechanismus soll darüber hinaus eine automatische, über den Quelltext hinausgehende Dokumentation auslösen. Zu diesem Zweck willst Du einen anderen Mechanismus missbrauchen, der dazu gedacht ist, auf überprüfbare Weise eine Bedingung zuzusichern. Eine Vorbedingung kannst Du nur garantieren, wenn Du selbst für ihre Einhaltung sorgen kannst. Denn zweiten Teil eines Kontraktes, nämlich die Nachbedingung unter der Vorbedingung, kannst Du wirklich garantieren.

Stell Dir vor, ich biete folgenden Kontrakt an: Wenn Du mir eine vollstaendige Spezifikation eines Softwaresystems lieferst, die in Form von UnitTests und AkzeptanzTests vorliegt, dann garantiere ich, dass das gelieferte Softwaresystem diese Spezifikation einhält. Darüber hinaus garantiere ich, dass die von Dir in dieser Form gelieferte Spezifikation vollständig sein wird. Würde Dir das nicht eigenartig vorkommen? Würdest Du jemanden, der Dir die zweite Garantie zu machen bereit ist, nicht für völlig naiv halten? -- kw

Worin besteht der Sinn, die Einhaltung des Kontraktes zu überprüfen und was passiert, wenn die Vorbedingung nicht eingehalten wird? Die Überprüfung dient dazu, mögliche Fehler einzugrenzen. Ist die Vorbedingung nicht erfüllt, liegt der Fehler beim Aufrufer. Wenn die Bedingung nicht erfüllt ist und nicht überprüft wird, kann es passieren, dass wir den Fehler erst später feststellen und dass die Fehlersuche nicht so einfach wird. Ob die Überprüfung der Vorbedingung immer aktiviert sein sollte, hängt von deren Kosten ab. Wenn die Vorbedingung einer Binärsuche eine sortierte Liste ist, wäre es ziemlich sinnlos sie immer zu überprüfen und so die Performance von logaritmisch auf linear zu degradieren. Wenn der Aufrufer den Kontrakt bricht, bekommt er halt nicht das richtige Ergebnis. Während der Entwickler des Aufrufers den Fehler sucht, kann es sinnvoll sein, die Überprüfung zu aktivieren. --gR

Mißverständnisse
Das Vorhandensein einer Debug- und einer Release-Version hat in der (praktischen) Informatik eine lange Tradition, ich denke das ist kein Argument.
Ich finde auch, daß die Assertions beim Kunden nichts mehr bringen. Aber wie wär's denn mit der Kombination aus TestgetriebeneEntwicklung und Assertions ? Die TestgetriebeneEntwicklung liefert die kritischen Konfigurationen und die Assertions helfen bei der Fehlerlokalisation ? -- mj

Im übrigen kann ich das GanzOderGarNicht Prinzip nicht als Contragrund nachvollziehen. GanzOderGarNicht ist niemals anwendbar, nichts ist absolut perfekt ... oder willst du z.B. auch nur für das einfachste HelloWorld einen erschöpfenden Test schreiben :-) ?

Diskussionen

Verweise

In Bezug auf NULL als Eingabewert siehe NullAlsInputParameter.

Eine eher skeptische Einstellung zu Assertions vertrete ich in AssertionVermeidung.
Wegen der Ausführlichkeit meiner Stellungnahme hielt ich eine eigene Seite für sinnvoll. -- kg


KategorieC KategorieCee KategorieCpp AbgrenzungTestenZuAssertZuDesignByContract
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 29. November 2007 8:38 (diff))
Suchbegriff: gesucht wird
im Titel
im Text