TDD with Contracts ein SOLIDer Ansatz Scrum Day 2015 Hagen Buchwald, Sheip Dargutev, Stefan Pölz VKSI SIG C4J - Special Interest Group Contracts for Java des Vereins Karlsruher Software Ingenieure VKSI Verein Karlsruher Software Ingenieure www.vksi.de
Inhaltsübersicht TDD with Contracts ein SOLIDer Ansatz 1. Prolog 2. Einführung in die Theorie von Contracts 3. TDD with Contracts 4. Excours: TDD with Contracts ein SOLIDer Ansatz 5. Epilog 2 08.07.2015
OnGames PokerEngine: PokerBots simulieren Spieler und testen die Poker-Engine bei aktivierten Verträgen 3 08.07.2015 www.vksi.de SIG Contracts for Java
Wirkung des Einsatzes von Testing by Contract mit C4J und PokerBots auf die Qualität der bwin PokerEngine bwin PokerEngine ohne C4J Fehlermeldungen pro Betriebsmonat bwin PokerEngine mit C4J Fehlermeldungen nach 38 Betriebsmonaten 4 08.07.2015 www.vksi.de SIG Contracts for Java
Inhaltsübersicht TDD with Contracts ein SOLIDer Ansatz 1. Prolog 2. Einführung in die Theorie von Contracts 3. TDD with Contracts 4. Excours: TDD with Contracts ein SOLIDer Ansatz 5. Epilog 5 08.07.2015
Was ist Objektorientierte Programmierung? Definition nach Alan Kay (2003): OOP to me means only - messaging, - local retention and protection and hiding of state-process, - and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I m not aware of them. One of the things I should have mentioned is that there were two main paths that were catalysed by Simula. The early one was the bio/net non-dataprocedure route that I took. The other one, which came a little later as an objects of study was abstract data types, and this got much more play. 6 08.07.2015 Quelle: http://www.purl.org/stefan_ram/pub/doc_kay_oop_en
Objekt-Orientierte Programmierung (OOP). Definition nach Bertrand Meyer (1988) Ein Softwaresystem besteht aus Prozessoren, die Methoden auf Datenstrukturen ausführen. Methoden Was ist die Hauptfunktion des Systems? Datenstrukturen ADT Objekt Auf was arbeitet das System? Prozessoren Object OOP ist die Erstellung von Softwaresystemen als strukturierte Sammlung von Implementierungen Abstrakter Datentypen (ADTs). 7 08.07.2015 Quelle: Bertrand Meyer, Object-Oriented Software Construction, 1988
Was ist ein Abstrakter Datentyp (ADT)? Ein Abstrakter Datentyp (Abstract Data Type) besteht aus vier Teilen: 1) TYP 2) METHODEN 3) VORBEDINGUNGEN 4) NACHBEDINGUNGEN 1) AccountSpec 2) void deposit(int amount) void withdraw(int amount) int getbalance() 3) deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: --- 4) deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 8 08.07.2015 In Anlehnung an: Bertrand Meyer, Object-Oriented Software Construction, 1988
Das Prinzip eines Vertrags: Die Rechte des Kunden sind die Pflichten des Anbieters und umgekehrt! Kunde Rechte Anbieter Rechte Pflichten Pflichten 9 08.07.2015
Beispiel eines Vertrags: Ein Kunde kauft ein Brötchen beim Anbieter. Kunde Rechte Ich bekomme ein Brötchen geliefert. Anbieter Rechte Ich bekomme 50 Cent bezahlt. Pflichten Ich muss 50 Cent zahlen. Pflichten Ich muss ein Brötchen liefern. 10 08.07.2015
Es reicht aus, den Vertrag bei dem Anbieter zu hinterlegen und dort auch zu überprüfen. Kunde Rechte Anbieter Rechte Pflichten Pflichten 11 08.07.2015
Im Brötchen-Beispiel reicht es aus, den Vertrag beim Anbieter zu hinterlegen und zu überprüfen. Kunde Rechte Ich bekomme ein Brötchen geliefert. Anbieter Rechte Ich bekomme 50 Cent bezahlt. Pflichten Ich muss 50 Cent zahlen. Pflichten Ich muss ein Brötchen liefern. 12 08.07.2015
ADT Account : Die Vor- und Nachbedingungen stellen einen Vertrag dar. Es genügt, diesen Vertrag auf Anbieterseite zu definieren und zu prüfen. Kunde Rechte Pflichten 1) Account 2) void deposit(int amount) void withdraw(int amount) int getbalance() 3) deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: --- 4) deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 Anbieter Rechte Pflichten Die Vorbedingungen definieren die Rechte des Anbieters (und damit die Pflichten des Kunden). Die Nachbedingungen definieren die Pflichten des Anbieters (und damit die Rechte des Kunden). 13 08.07.2015
Kann man in Java Objekte als Implementierungen Abstrakter Datentypen beschreiben? TYP AccountSpec METHODEN void deposit(int amount) void withdraw(int amount) int getbalance() VORBEDINGUNGEN deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: ---? NACHBEDINGUNGEN deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 14 08.07.2015
Der Typ AccountSpec als Abstrakter Datentyp (ADT) in Java. TYP AccountSpec METHODEN void deposit(int amount) void withdraw(int amount) int getbalance() VORBEDINGUNGEN deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: --- NACHBEDINGUNGEN deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 15 08.07.2015
Der Standard-Konstruktor wird durch die Typen-Invariante als allgemeine Nachbedingung aller Methoden überprüft. TYP AccountSpec METHODEN void deposit(int amount) void withdraw(int amount) int getbalance() VORBEDINGUNGEN deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: --- NACHBEDINGUNGEN deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 typeinvariant: getbalance >= 0 16 08.07.2015
Die Vorbedingungen eines überladenen Konstruktors werden in der Vertragsklasse der Implementierung geprüft. TYP AccountSpec METHODEN void deposit(int amount) void withdraw(int amount) int getbalance() Account VORBEDINGUNGEN deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: --- Account: amount >= 0 Account NACHBEDINGUNGEN deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 typeinvariant: getbalance >= 0 17 08.07.2015
Für Legacy Systeme kann der Softwarevertrag ohne Codeänderung von außen an die Klasse gehängt werden. TYP AccountSpec METHODEN void deposit(int amount) void withdraw(int amount) int getbalance() Account VORBEDINGUNGEN deposit: amount > 0 withdraw: amount > 0 getbalance >= amount getbalance: --- Account: amount >= 0 Account NACHBEDINGUNGEN deposit: getbalance == old getbalance + amount withdraw: getbalance == old getbalance amount getbalance: result >= 0 typeinvariant: getbalance >= 0 18 08.07.2015
custom doclet 19 08.07.2015
Das C4J Plugin erlaubt u.a. das Text Hovering: Entweder im JavaDoc-Format Eclipse Plugin 20 08.07.2015
Das C4J Plugin erlaubt u.a. das Text Hovering: oder im Quellcode-Format. Eclipse Plugin 21 08.07.2015
Inhaltsübersicht TDD with Contracts ein SOLIDer Ansatz 1. Prolog 2. Einführung in die Theorie von Contracts 3. TDD with Contracts 4. Excours: TDD with Contracts ein SOLIDer Ansatz 5. Epilog 22 08.07.2015
TDD Cycle make it red: schreibe einen Test, der fehlschlägt make it green: schreibe gerade so viel Code, dass alle Tests laufen make it right: beseitige duplizierten Code und andere code smells 23 08.07.2015
TDD with Contracts Unit Test TDD Class Interface Unit Contract package bank; public class AccountTest { private AccountSpec account = new Account(); @AfterClass public static void report() { System.out.println("AssertCounter: " + AssertCounter.getCounter()); @Test public void newaccount_shouldhavebalancezero() { checkbalance(0); positive @Test public void deposit_shouldupdatebalance() { account.deposit(10); checkbalance(10); Tests @Test public void repeateddeposit_shouldupdatebalance() { account.deposit(10); account.deposit(30); checkbalance(40); @Test public void depositandwithdraw_shouldupdatebalance() { account.deposit(15); account.withdraw(5); checkbalance(10); @Test(expected = AssertionError.class) public void depositnegativeamount_shouldfail() { account.deposit(-45); @Test(expected = AssertionError.class) public void withdrawnegativeamount_shouldfail() { account.withdraw(-5); negative @Test(expected = AssertionError.class) public void withdrawamountgreaterthanbalance_shouldfail() { account.deposit(10); Tests account.withdraw(20); private void checkbalance(int expectedamount) { AssertCounter.increment(); assertequals(expectedamount, account.getbalance()); package bank; public class Account implements AccountSpec { private int balance; @Override public int getbalance() { return balance; @Override public void deposit(int amount) { balance += amount; @Override public void withdraw(int amount) { balance -= amount; Clean Code package bank; import de.vksi.c4j.contractreference; import de.vksi.c4j.pure; @ContractReference(AccountSpecContract.class) public interface AccountSpec { void deposit(int amount); void withdraw(int amount); @Pure int getbalance(); package bank; Pre-Condition public class AccountSpecContract implements AccountSpec { @ClassInvariant public void classinvariant() { assert target.getbalance() >= 0 : "balance >= 0"; @Override Post-Condition public void deposit(int amount) { if (precondition()) { assert amount > 0 : "amount > 0"; if (postcondition()) Pre-Condition { assert target.getbalance() == old(target.getbalance()) + amount; @Override Post-Condition public void withdraw(int amount) { if (precondition()) { assert amount > 0 : "amount > 0"; assert target.getbalance() >= amount : "balance >= amount"; Pre-Condition if (postcondition()) { assert target.getbalance() == old(target.getbalance()) - amount; Post-Condition @Override public int getbalance() { return ignored(); Clean Contract Klassen-Ebene (Implementierung) Typen-Ebene (Abstrakter Datentyp) 24 08.07.2015
Clean Contract: Wie formuliert man einen vollständigen Vertrag? Die 6 Prinzipien der Vertragserstellung Prinzip 6 DRY: Formuliere Invarianten um Nachbedingungen, die für alle Methoden gelten, nur ein einziges Mal an zentraler Stelle zu formulieren. Damit werden die unveränderlichen Eigenschaften des Objekts beschreiben. Prinzip 5 PRE: Prüfe für jede Abfrage und jedes Kommando, ob eine Vorbedingung erforderlich ist. Die Vorbedingung beantwortet die Frage: Kann diese Methode zu jeder Zeit aufgerufen werden oder nur unter ganz bestimmten Voraussetzungen? Prinzip 4 Prinzip 3 Prinzip 2 POST: Erstelle für jedes Kommando eine Nachbedingung, die den Effekt des Kommandos auf den sichtbaren Zustand des Objekts mit Hilfe aller elementaren Abfragen beschreibt. Erstelle für jede abgeleitete Abfrage eine Nachbedingung, die beschreibt, welches Ergebnis zurückgegeben wird, ausgedrückt durch eine oder mehrere elementare Abfragen. Trenne elementare Abfragen von abgeleiteten Abfragen. Abgeleitete Abfragen können durch elementare Abfragen ausgedrückt werden. CQS: Trenne Abfragen durch @Pure von Kommandos. Prinzip 1 Abfragen geben ein Ergebnis zurück, ändern jedoch nicht die sichtbaren Eigenschaften des Objekts. Kommandos können das Objekt verändern, geben jedoch kein Ergebnis zurück. 25 08.07.2015 In enger Anlehnung an: R. Mitchell / J. McKim: Design by Contract, by Example
Inhaltsübersicht TDD with Contracts ein SOLIDer Ansatz 1. Prolog 2. Einführung in die Theorie von Contracts 3. TDD with Contracts 4. Excours: TDD with Contracts ein SOLIDer Ansatz 5. Epilog 26 08.07.2015
Was ist Clean Code? Robert C. Martin, author of Clean Code a handbook of Agile Software Craftmanship: Clean Code is a school of thoughts. Smells & Heuristics Meaningful Names DRY Don t Repeat Yourself! CQS Command Query Separation 27 08.07.2015
Clean Code: Wie schreibt man qualitativ hochwertige Software? Die 5 SOLID-Prinzipien Initial Stands for (acronym) Concept S SRP [4] Single responsibility principle a class should have only a single responsibility (i.e. only one potential change in the software's specification should be able to affect the specification of the class) O OCP [5] Open/closed principle software entities should be open for extension, but closed for modification. L LSP [6] Liskov substitution principle objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program. See also design by contract. I ISP [7] Interface segregation principle many client-specific interfaces are better than one general-purpose interface. [8] D DIP [9] Dependency inversion principle one should Depend upon Abstractions. Do not depend upon concretions. [8] 28 08.07.2015 Quelle: https://en.wikipedia.org/wiki/solid_%28object-oriented_design%29
Was ist Clean Code? Ron Jeffries, author of Extreme Programming Installed: Abstraction often calls my attention to what s really going on. Reduced duplication, high expressiveness, and early building of simple abstractions: That s what makes clean code for me. 29 08.07.2015
Dependency Inversion Principle (DIP) Depend upon abstractions. Do not depend upon concretions (implementations). Joshua Bloch spricht in seinem Buch Effective Java (Second Edition, 2008) insgesamt 78 Punkte (Items) an, wie man mit Java programmieren sollte, um hohe Qualität zu erzeugen. Item 19: Use interfaces only to define types When a class implements an interface, the interface serves as a type that can be used to instances of the class. It is inappropriate to define an interface for any other purpose. Item 40: Design method signatures carefully Use interfaces rather than classes as parameter types. Item 52: Refer to objects by their interfaces If appropriate interfaces types exist, then parameters, return values, variables and fields should all be declared using interfaces types. If you get into the habit of using interfaces as types, your program will be much more flexible. 30 08.07.2015
SRP Singe Responsibilty Principle 31 08.07.2015
OCP Open Closed Principle 32 08.07.2015
LSP Liskov Substitution Principle 33 08.07.2015
ISP Interface Segregation Principle 34 08.07.2015
Was ist Clean Code? Ron Jeffries, author of Extreme Programming Installed: Abstraction often calls my attention to what s really going on. Reduced duplication, high expressiveness, and early building of simple abstractions: That s what makes clean code for me. 35 08.07.2015
Inhaltsübersicht TDD with Contracts ein SOLIDer Ansatz 1. Prolog 2. Einführung in die Theorie von Contracts 3. TDD with Contracts 4. Excours: TDD with Contracts ein SOLIDer Ansatz 5. Epilog 36 08.07.2015
Einsatz von Softwareverträgen seit 2006 (bwin, Stockholm) Lessons Learned 1. Pareto-Prinzip: 20% der Klassen stellen 80% der genutzten Funktionalität zur Verfügung. Es reicht aus, diese Kernklassen mit Softwareverträgen zu schützen. 2. Falsche Annahmen als Hauptfehlerquelle: 80% der bei bwin gefundenen Fehler waren auf Verletzungen von Vorbedingungen (und Klasseninvarianten) zurückzuführen. Der Grund waren falsche bzw. fehlende Annahmen darüber, wie eine Klasse genutzt werden sollte. 3. Autorenschaft schützt nicht vor falschen Annahmen: Falsche Annahmen unterliefen auch den Autoren der Klassen. Nach nur wenigen Wochen Arbeit an anderen Modulen hatten sie die Annahmen vergessen, unter denen sie die Klassen erstellt hatten. 4. Testing by Contract: Die Kombination von Softwareverträgen mit PokerBots virtuellen Spielern, die über Nacht die aktuelle Version der PokerEngine nutzten erwies sich als äußerst wirkungsvolles Instrument, um subtile Fehler aufzuspüren (Boundary Conditions, Edge Conditions, Corner Conditions). 5. Macht der Gewohnheit: Trotz dieser Erfolge blieb die Nutzung von Softwareverträgen auf das PokerEngine-Team beschränkt. Andere Teams zogen nicht nach. 37 08.07.2015 Quelle: Interview mit Jonas Bergström, Sept. 2012, Stockholm www.vksi.de SIG Contracts for Java
CQS Command Query Separation Zentrales Prinzip für TDD with Contracts 38 08.07.2015
OOP ist die Erstellung von Software- Systemen als strukturierte Sammlung von Klassen. Eine Klasse ist eine (von vielen möglichen) Implementierung eines Typs. Typen werden spezifiziert durch Abstrakte Datentypen (ADT). 39 08.07.2015
OOP ist die Erstellung von Software- Systemen als strukturierte Sammlung von Implementierungen Abstrakter Datentypen (ADT). 40 08.07.2015
TDD with Contracts ein SOLIDer Ansatz Anhang: Die C4J Syntax mit Beispielen VKSI SIG C4J - Special Interest Group Contracts for Java des Vereins Karlsruher Software Ingenieure VKSI Verein Karlsruher Software Ingenieure www.vksi.de
Contracts for Java: C4J 6.0 Die Syntax C4J 6.0 : Annotationen @ContractReference @Contract (externer Vertrag) @Target @ClassInvariant Erläuterung Markieren einer Klasse oder eines Interfaces als geschützt durch die angegebene Vertragsklasse. Markieren einer Klasse als Vertragsklasse. Die zu schützende Klasse wird durch die Vererbungsbeziehung definiert. Dieser indirekte Ansatz ermöglicht externe Vertragsklassen, die ohne Veränderung des Quellcodes der zu schützenden Klasse von außen eingebracht werden können, z.b. bei Legacy-Systemen. Markieren einer Instanzvariablen namens target als Referenz auf das zu schützende Objekt. Markieren einer Methode als Klasseninvariante. @Pure @PureTarget (externer Vertrag) Markieren einer Methode als seiteneffektfreie Abfrage (in Abgrenzung zu Kommandos). Ist die @Pure- Überprüfung aktiviert, überprüft C4J zur Laufzeit automatisch die Methode auf Seiteneffektfreiheit. 42 08.07.2015 Hagen Buchwald, Ben Romberg Contracts for Java C4J 6.0 VKSI
Contracts for Java: C4J 6.0 Die Syntax anhand von Beispielen C4J 6.0 : Annotationen @ContractReference @Contract (externer Vertrag) @Target @ClassInvariant @Pure @PureTarget (externer Vertrag) Beispiel @ContractReference(PointSpecContract.class) public interface PointSpec @Contract(forTarget = PointSpec.class) public class PointSpecContract @Target private PointSpec target; @ClassInvariant public void classinvariant() { " assert target.getx() >= 0 : "x >= 0"; @Pure public int getx(); @Override @PureTarget public int getx() { 43 08.07.2015 Hagen Buchwald, Ben Romberg Contracts for Java C4J 6.0 VKSI
Contracts for Java: C4J 6.0 Die Syntax C4J 6.0 : statische Methoden precondition() postcondition() result() ignored() old() unchanged() Erläuterung Vorbedingung einer Methode (Pre-Condition). Nachbedingung einer Methode (Post-Condition). Rückgabewert der Methode (nur in Post-Conditions verfügbar). Liefert einen Dummy-Rückgabewert und wird benötigt, wenn die zu schützenden Methode in der zu schützenden Klasse (target class) einen Rückgabewert besitzt (Query). Abfragen des Zustands eines Objekts vor der Methodendurchführung (nur in Post-Conditions verfügbar). Überprüfung, ob der aktuelle Zustand des Objekts noch unverändert gegenüber dem Zustand des Objekts vor der Methodendurchführung ist (nur in Post-Conditions und bei aktivierter @Pure- Überprüfung verfügbar). 44 08.07.2015 Hagen Buchwald, Ben Romberg Contracts for Java C4J 6.0 VKSI
Contracts for Java: C4J 6.0 Die Syntax anhand von Beispielen C4J 6.0 : statische Methoden Beispiel precondition() public void setx(int x) { if (precondition()) { assert x >= 0 : "x >= 0"; postcondition() if (postcondition()) { assert target.getx() == x : "x set"; result() public int getx() { if (postcondition()) { int result = result(); assert result >= 0 : "result >= 0"; ignored() return ignored(); 45 08.07.2015 Hagen Buchwald, Ben Romberg Contracts for Java C4J 6.0 VKSI
Contracts for Java: C4J 6.0 Die Syntax anhand von Beispielen C4J 6.0 : statische Methoden Beispiel old() if (postcondition()) { assert target.size() == old(target.size()) + 1 : "size increased"; unchanged() if (postcondition()) { assert unchanged(target.size()) : "size "; 46 08.07.2015 Hagen Buchwald, Ben Romberg Contracts for Java C4J 6.0 VKSI