Scala Grundkurs

Pre- und Postconditions

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
Mit Pre- und Postconditions, also Vor- und Nachbedingungen, lässt sich prüfen, ob Ein- und Ausgabewerte von Funktionen und Klassen den Erwartungen entsprechen.

Transkript

Oftmals ist es schwierig, beim Nutzen fremder Methoden und Funktionen zu verstehen, welche Art von Eingaben und Ausgaben legitim sind. In Programmiersprachen wie Eiffel hat sich dafür ein Mechanismus entwickelt, diesen bezeichnet man allgemein auch als kontraktbasierte Entwicklung. Innerhalb dieser Art der Entwicklung definiere ich für meine Eingaben meine Ausgaben als auch für Zwischendinge Bedingungen, die immer gelten müssen. Sogenannte Pre-, Post- und Intermediate Conditions. Während Programmiersprachen wie Java dafür nur die reguläre Assertion kennen, kann man innerhalb von Scala dafür besondere Mechanismen nutzen. Und ich zeige Ihnen nun, wie Sie das innerhalb einer eigenen Applikation anwenden können. In meiner Applikation befindet sich nun ein Kunde, dieser Kunde hat ein Bankkonto, er hat einen Warenkorb und kauft Produkte und bezahlt diese dann im Online-Shop. Allerdings gibt der Kunde hier im Moment eine völlig falsche IBAN an, und so wäre es nun sinnhaft, dass beim Erzeugen eines Checking Accounts überprüft wird, ob die IBAN legitim ist. Dafür verwende ich also die "case class" "CheckingAccount" und möchte nun eine Bedingung an den Parameter "iban" stellen. Das Ganze möchte ich nun mit Hilfe einer Regular Expression tun. Dafür definiere ich zunächst einmal eine Regular Expression-Konstante. Dafür benötige ich zusätzlich also ein "companion object" mit der Bezeichnung "Checking Account". Innerhalb dessen führe ich nun eine private Konstante ein, mit der Bezeichung "IBAN_REGEX". Hier füge ich nun die Regular Epression ein, um zu überprüfen, dass es sich um eine legitime IBAN handelt. Als nächstes verwandle ich das Ganze noch in einen Regular Expression-Ausdruck. Mit Hilfe des Ausrufes von ".r". Da ja nun alles, was innerhalb meiner "case class" steht, dem Konstruktor dient, kann ich also unmittelbar eine Bedingung an die IBAN stellen. Dies tue ich mit der Methode "require". Innerhalb der Methode "require" definiere ich also nun eine Bedingung, die gelten muss, wenn ein solcher "Checking Account" angelegt wird. In dem Fall also "Checking Account.IBAN_REGEX. findFirstMatchIn(iban)", und immer nur dann, wenn ein Match gefunden wird, also es definiert ist, dann ist diese Bedingung wahr. Weiterhin kann ich nun auch eine Fehlermeldung definieren, die, sollte das Ganze nicht zutreffen, auf der Konsole beispielsweise sich wiederspiegelt. Ich trage hier nun "Invalid IBAN" ein. Als nächstes starte ich nun die Applikation. In dem Fall erhalte ich nun also die Fehlermeldung "IllegalArgumentException: requirement failed: Invalid IBAN". Trage ich stattdessen eine korrekte IBAN ein, beispielsweise mit dieser hier, so verschwindet diese Fehlermeldung, und die Bedingung ist wahr. Dabei handelt es sich nun also um eine Bedingung für ein Argument oder auch eine Vorbedingung. Jeder, der diese Klasse nun sehen kann, versteht also, was die Anforderungen für den Parameter IBAN sind. Als nächstes könnte ich nun noch eine Intermediate Condition definieren. Beispielsweise möchte ich überprüfen, dass ein Warenkorb nicht leer ist. Beziehungsweise, dass die Summe aller Preise nicht null sein darf. Aktuell berechne ich innerhalb der Funktion "checkout" bereits einen "Basket Price". Also der Gesamtpreis aller Produkte in einem Warenkorb. Als nächstes kann ich eine sogenannte Assumption einführen, also eine Intermediate Condition. Meine Assumption nimmt wiederum eine Bedingung, ich kann nun also hier festlegen, dass ich beispielsweise erwarte, dass der Preis des Warenkorbs auf jeden Fall größer als null ist. Schließlich möchte ich weder dem Kunden Geld geben noch möchte ich nichts verkaufen. Ich definiere zusätzlich wiederum eine Fehlermeldung, zum Beispiel "Price of basket can not be 0 or lower". Wenn ich nun beispielsweise einen leeren Warenkorb erstelle, mit der Bezeichnung "emptyBasket", und diesen in den "checkout"-Prozess schicke, über "OnlineShop.default.checkout" "(emptyBasket)", bekomme ich auch wiederum hier eine Fehlermeldung. Diesmal basierend auf einem "Assertion Error". Doch was ist nun, wenn ich beispielsweise eine Post Condition definieren möchte. Post Conditions machen immer dann Sinn, wenn man vor allem Methoden oder Funktionen verwendet, die man selbst nicht kontrolliert. Innerhalb meiner "case class" "Price" mache ich aktuelle eine Konvertierung von EUR beispielsweise zu USD. Ich könnte diese Logik nun beispielsweise auch auslagern in eine andere Klasse. Dafür erstelle ich nun ein "object" mit der Bezeichung "CurrencyConverter". Ein solcher "CurrencyConverter" hat also eine statische Methode mit der Bezeichnung "eurtoUsd". Er nimmt ein "Double" entgegen, gibt wiederum ein "Double" aus, und das entsteht durch die Multiplikation mit dem Währungskurs 1,07. Innerhalb meiner "case class" "Price" tausche ich nun die Implementierung aus. In dem Fall verwende ich also nun die Hilfsfunktion auf dem "CurrencyConverter" "eurtoUsd" und übergebe hier lediglich das "value". Das Problem könnte nun beispielsweise sein, dass ich den Inhalt dieser Funktion nicht kontrolliere und nicht genau weiß, was dabei herauskommt. Ich möchte allerdings sicherstellen, dass niemals ein Preis unter 0 oder genau 0 entstehen darf. Dafür benutze ich also nun eine Post Condition, so kann ich nun also auf der Ausgabe dieser Funktion "toUsd" zusätzlich einen Aufruf tätigen. Hierbei auf die Funktion "ensuring". "ensuring" nimmt also eine Lambda entgegen, in der ich als Argument das Resultat bekomme, das eigentlich hätte ausgegeben werden sollen, so kann ich nun beispielsweise noch eine Überprüfung darauf stattfinden lassen. In meinem Fall überprüfe ich also, ob der Wert größer/gleich null ist "(=0)" ist. Und ähnlich wie bei "require" und "assume" kann ich also hier auch eine Nachricht definieren, die falls die Bedingung nicht zutrifft, ausgegeben wird. In dem Fall also beispielsweise "Price can not be below 0 after conversion". Das Ganze möchte ich nun noch testen. Beispielsweise mithilfe eines "println"-Aufrufs auf die Erzeugung eines Preises in Höhe von "10, "EUR"", den ich dann in USD konvertieren möchte. Im Normalfall funktioniert das Ganze wunderbar. Sollte ich jetzt allerdings etwas an der Logik des Currency Converter ändern, beispielsweise durch eine negative Multiplikation, so garantiert mir nun meine Post Condition, dass der Preis nicht unter 0 sein darf. Die soeben von mir gezeigten Methoden und Funktionen sind Teil des Scala PreDef Package, das bedeutet, dass sie ähnlich wie der Aufruf von "println" automatisch innerhalb jeder Scala-Klasse definiert sind. Es ist also nicht nötig, diese zusätzlich zu importieren. Diese Assertion-Statements lassen sich dann auch beispielsweise beim Kompilieren deaktivieren. Wenn man also solche Assertions beispielsweise nur zur Dokumentation oder innerhalb von Test cases nutzen möchte, kann man den Scala Compiler dazu anweisen, dass die Assertions bei der Kompilation aus dem Code herausextrahiert werden. Somit gehen auch keine Performanceverluste damit einher, dass man das Ganze in Produktion ausführen möchte. Mithilfe so genannter Messages kann man Hinweise darauf geben, warum es Probleme gibt, wenn eine solche Methode ausgeführt wird und eine Bedingung nicht zutrifft. Das sollte man nach Möglichkeit immer tun, um denjenigen, der den Fehler verursacht, auch zu signalisieren, woran das Ganze liegt. Gesehen haben Sie also zunächst einmal die Methode "require". Diese stellt also eine Vorbedingung dar oder auch Precondition. Das heißt, sie wird im Normalfall dann angewendet, wenn man Argumente überprüfen will. In dem Fall gibt sie dann auch eine entsprechende Fehlermeldung aus, in Form einer InvalidArgumentException. Bei der Methode "assume" oder "assert" handelt es sich um eine Zwischenbedingung oder auch Intermediate Condition. Dabei handelt es sich ähnlich wie in Java um einen ganz regulären AssertionError. Zu guter Letzt gibt es noch die Schlussbedingung mit der Bezeichnung "ensuring". Das Besondere daran ist, dass man einen Rückgabewert innerhalb einer Funktion ausgeben kann und trotzdem noch zusätzlich eine Überprüfung darauf vornimmt. In Programmiersprachen wie Java, die einen unmittelbaren Return haben, beispielsweise, gestaltet sich das als schwierig.

Scala Grundkurs

Entdecken Sie die Möglichkeiten und Eigenschaften der modernen Programmiersprache Scala.

4 Std. 44 min (39 Videos)
Derzeit sind keine Feedbacks vorhanden...
Software:
Exklusiv für Abo-Kunden
Erscheinungsdatum:12.04.2017

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!