101
Clean Code Gruppe Palme Lukas, Eric, Paul, Matthias, André

Clean Code - ziemers.de€¦ · - Convention over Configuration Was ist Clean Code? sichtbar professionell ... A Handbook of Agile Software Craftsmanship 8 - nicht als positiv anzusehen

Embed Size (px)

Citation preview

Clean Code

Gruppe PalmeLukas, Eric, Paul, Matthias, André

Gliederung

1. Einführung2. Kommentare3. Namen4. Funktionen5. Objekte und Datenstrukturen6. Fehler-Handling7. Unit-Tests8. Klassen9. Nebenläufigkeit

2

EinführungSimple and clean

1.

Das Standard-werk!Author Robert C. Martin

4

“Truth can only be found in one place: the code.”

5

“It is not enough for code to work.”

intuitiv verständlichdurch Regeln wie

- Code Conventions- Design Patterns- Convention over

Configuration

Was ist Clean Code?

sichtbar professionellProfessionalität

= Bewusstheit + Prinzipien

6

= strukturierter Ausdruck von Funktionalität

Kommentare,,Kommentare können beides sein, hilfreich und hinderlich”

2.

“Nur der Code kann wirklich sagen, was er tut. Er ist die einzige Quelle für wirklich genaue

Informationen. Deshalb sollten wir uns anstrengen, Kommentare [...] zu minimieren. ”

― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

8

- nicht als positiv anzusehen- müssen sinnvoll eingesetzt werden

ÜberlegungKommentar schreiben

ODER

Code ausdrucksstärker schreiben

Kommentare

9

Beispiel

//Check if it is working timeif($day < 6 && $day > 0 && $time <= 18 && $time >= 9){

if(isWorkingTime()){

10

GUT- erklärend- juristisch- informativ- Markierung

- kurz

Gute Kommentare schlechte Kommentare

SCHLECHT- redundant- auskommentierter Code- Erklärungen zu kurzem

Code

- überflüssig

11

Zusammenfassung (Kommentare)◉ kein Ausgleich für schlechten Code◉ nicht immer sinnvoll◉ Gute Kommentierung = kurz und prägnant

12

NamenNicht so leicht wie man denkt

3.

14

public List <int[ ]> getThem( ) {

List <int[ ]> list1 = new Arraylist <int[ ]> ( );

for (int [ ] x : thelist) {

if (x [0] == 4){

list1.add (x);

}

}

return list1;

}

Ein Beispiel - (1 min)

Na,alles klar?Worum geht´s?

15

Was ist das Problem/Fragen?

theList

Welche Dinge werden hier gespeichert?

4

Welche Bedeutung hat die 4?

list1

Wie wird die Rückgabeliste verwendet?

16

public List <int[ ]> getThem( ) {

List <int[ ]> list1 = new Arraylist <int[ ]> ( );

for (int [ ] x : thelist) {

if (x [0] == 4){

list1.add (x);

}

}

return list1;

}

17

public List <int[ ]> getFlaggedCells( ) {

List <int[ ]> flaggedCells = new Arraylist <int[ ]> ( ) ;

for(int[ ] cell : gameBoard){

if(cell [STATUS_VALUE] == FLAGGED){

flaggedCells.add(cell);

}

}

return flaggedCells;

}

Ein Beispiel - (Fortsetz.)

Ein Vergleich

theList

Welche Dinge werden hier gespeichert?

4

Welche Bedeutung hat die 4?

list1

Wie wird die Rückgabeliste verwendet?

18

public List <int[ ]> getThem( ) {

List <int[ ]> list1 = new Arraylist <int[ ]> ( );

for (int[ ] x : thelist) {

if (x [0] == 4){

list1.add (x);

}

}

return list1;

}

public List <int[ ]> getFlaggedCells( ) {

List <int[ ]> flaggedCells = new Arraylist <int[ ]> ( ) ;

for(int[ ] cell : gameBoard){

if(cell [STATUS_VALUE] == FLAGGED){

flaggedCells.add(cell);

}

}

return flaggedCells;

}

Evaluation

1. Code ist expliziter

er drückt insgesamt deutlich besser aus, was er macht

2. Komponenten sind selbstbeschreibend

es wird auch im Einzelnen klarer, was jeder Komponent tut

3. Kontext/Verständnis

...wird schon anhand der Namensgebungen klarer. Es könnte sich um ein Spiel handeln.

Mine Sweeper19

public List <int[ ]> getFlaggedCells( ) {

List <int[ ]> flaggedCells = new Arraylist <int[ ]> ( ) ;

for(int[ ] cell : gameBoard){

if(cell [STATUS_VALUE] == FLAGGED){

flaggedCells.add(cell);

}

}

return flaggedCells;

}

20

public List <Cell> getFlaggedCells( ) {

List <Cell> flaggedCells = new Arraylist <int[ ]> ( ) ;

for(Cell cell : gameBoard){

if(cell.isFlagged()){

flaggedCells.add(cell);

}

}

return flaggedCells;

}

Ein Beispiel - (Fortsetz. 2)

public List <int[ ]> getFlaggedCells( ) {

List <int[ ]> flaggedCells = new Arraylist <int[ ]> ( ) ;

for(int[ ] cell : gameBoard){

if(cell [STATUS_VALUE] == FLAGGED){

flaggedCells.add(cell);

}

}

return flaggedCells;

}

>>kleine Hilfsklasse Cell (mit aussagekräftigen Funktionen)

struktureller Eingriff, der sich auch positiv auf die Lesbarkeit/Namensgebung auswirkt

O & 0 || l & 1in vielen Schriftarten sehen l & 1 bzw. 0 und O sehr ähnlich aus

>>in Abhängigkeit der in der IDE eingestellten Schriftart kann Verwechslungsgefahr bestehen (Fehleranfälligkeit)

Grundsätzliche Probleme bei der Namensgebung

Variablennamenanderweitig, technisch konnotierte Variablennamen sollten nicht verwendet werden

>>Irritationen für andere Entwickler, die den Code lesen

21

Grundsätzliche Probleme bei der Namensgebung

Variablennamen (Länge)lokale Variablen (Bsp. in Zählschleife) kurz

for(int i = 0; i < 100;i++){ System.out.println("Durchlauf: " + i);}

22

Variablennamen (Länge)globale, funktionale Variablen länger (beschreibend)public class Global_Variable extends Application {

private String GlobalString;

public String getState(){return GlobalString;

}

public void setState(String GS){ GlobalString = GS;

}}

Programmieren ist eine soziale Aktivität

Robert C. Martin, Clean Code, S.50

23

aussprechbare Namen

private Date genymdhms (generate Year, Month, Day, Hour…)

vs.

private Date generationTimestamp

Grundsätzliche Probleme bei der Namensgebung

24

Abstrakte Konzepte konsistent beibehalten!

fetch, retrieve, get bedeuten allesamt “etwas holen”

getTimestamp getTimestamp

getDate vs. retrieveDate

getDelivery fetchDelivery

Grundsätzliche Probleme bei der Namensgebung

25

Zusammenfassung (Namen)◉ Beschreibungsfähigkeit des Programmieres

○ Kontexte durch Namensgebung○ Eindeutigkeit / Verständlichkeit

◉ Voraussetzung: Gemeinsamer kultureller Hintergrund (Risiko!)

26

Funktionen...erzählen Geschichten

4.

28

public static String testableHtml (PageData pageData ,boolean includeSuiteSetup) throws Exception {WikiPage wikiPage = pageData.getWikiPage();StringBuffer buffer = new StringBuffer();if(pageData.hasAttribute("Test")) {

if(includeSuiteSetup) {WikiPagesuiteSetup = PageCrawlerimpl.getinheritedPage

(SuiteResponder.SUITE_SETUP_NAME, wikiPage);if(suiteSetup != null) {

WikiPagePath pagePath = suiteSetup.getPageCrawler().getFullPath(suiteSetup);Stringpage PathName = PathParser.render(pagePath);buffer.append("!include -setup.")

.append(pagePathName)

.append("\n");}

}WikiPagesetup = PageCrawlerimp1.getinheritedPage("SetUp", wikiPage) ;if(setup != null) {

WikiPagePathsetupPath = wikiPage.getPageCrawler().getFullPath(setup);StringsetupPathName = PathParser.render(setupPath);buffer.append("!include -setup.")

.append(setupPathName)

.append("\n");}buffer.append(pageData.getContent());if(pageData.hasAttribute("Test")) {...............................................................................................

Ein Bsp.(2 min)

29

public static String renderPageWithSetupsAndTeardowns (PageData pageData , boolean isSuite) throws Exception {

boolean isTestPage = pageData.hasAttribute("Test");if(isTestPage) {

WikiPage testPage = pageData.getWikiPage();StringBuffer newPageContent = new StringBuffer();includeSetupPages(testPage, newPageContent, isSuite);newPageContent.append(pageData.getContent());includeTeardownPages(testPage , newPageContent, isSuite);pageData.setContent(newPageContent.toString());

}return pageData.getHtml();

}

Bsp. (gleiche Funktion)

Wodurch werden Funktionen gut lesbar? (Grobkonzept)

1. Größemax. 20-30 Zeilen, Zeilenlänge max 150 Zeichen, Einrücktiefe der Blöcke 1-2 Ebenen

2. nur eine Aufgabe“Funktionen sollten eine Aufgabe erfüllen. Sie sollten sie gut erfüllen. Sie sollten nur diese Aufgabe erfüllen.”

3. eine Abstraktionsebene je Funktionfolgt aus Punkt 2 > Anweisungen innerhalb der Funktion alle auf einer Abstraktionsebene (Größe/Reichweite der Anweisungen)

Stepdown-Regel:

Funktion ruft andere, eine Abstraktionsebene darunter liegende Funktion auf (Treppe runter gehen)

30

4. beschreibende NamenName sollte Aufgabe der Funktion beschreiben - Test: Wenn Funktion nur eine Aufgabe erfüllt, Namensgebung einfach > mehrere, aufeinander aufbauende Funktionen erzählen dann eine “Geschichte”

5. FunktionsargumenteBest Case kein Argument, häufig 1 Argument, mehr als 3 Argumente vermeiden

>>viele Argumente auch für Tests problematisch, da exponentiell steigende Anzahl an Testfällen

31

Wodurch werden Funktionen gut lesbar? (Grobkonzept)

Namensgebung (monadisch)

Verb Substantiv

32

Funktion Argumentwrite (name)

Argument-Objekte

33

(Triadische Funktion)

Circle makeCircle(double x, double y, double radius)

(Dyadische Funktion)

Circle makeCircle(Point center, double radius)

>>geringere Argumentanzahl = geringere Fehleranfälligkeit (Reihenfolge etc.)

DRY (Don´t Repeat Yourself!)

34

public class Mechanic {public void serviceCar() {

System.out.println("servicing car now");}

public void serviceBike() {System.out.println("servicing bike now");

}}

>>jede Funktion hat eine Aufgabe, alles gut!

DRY (Don´t Repeat Yourself!)

35

public class Mechanic {public void serviceCar() {

// washing vehicle hereSystem.out.println("servicing car now");// polishing vehicle here

}

public void serviceBike() {// washing vehicle hereSystem.out.println("servicing bike now");// polishing vehicle here

}}

>>Oje, zu jeder Funktion kommen weitere, immer gleiche Aufgaben hinzu (Always repeat yourself)

Probleme:

1. Code bläht sich auf2. Wartbarkeit schwierig

DRY (Don´t Repeat Yourself!)

36

public class Mechanic {public void serviceCar() {

System.out.println("servicing car now");performOtherTasks();

}

public void serviceBike() {System.out.println("servicing bike now");performOtherTasks();

}

public void performOtherTasks() {System.out.println("performing tasks other than servicing");// do whatever you want to do in the servicing package

}}

>>Besser, sich wiederholende Aufgaben in neue Funktion auslagern (Konzentration)

1. “schmalerer” Code2. gute Wartbarkeit (1 Punkt)

Try/Catch-Blöcke extrahieren

37

Try/Catch-Blöcke lenken oft von der eigentlichen Struktur des Codes ab

public void delete(Page page) {try {

deletePageAndAllReferences(page);}catch (Exception e) {

logError(e);}

}

>>Körper der Try/Catch-Blöcke in separate Funktionen

private void deletePageAndAllReferences(Page page) throws Exception {

deletePage(page);registry.deleteReference(page.name);configKeys.deleteKeys(page.name.makeKey());

}

private void logError(Exception e) {logger.log(e.getMessage());

}

Zusammenfassung (Funktionen)

38

Wenn ich Funktionen schreibe, sind sie zunächst lang und kompliziert. Sie haben lange Argumentenlisten. Die Namen sind willkürlich und es gibt duplizierten Code.

Dann massiere und verfeinere ich den Code, lagere Funktionen aus, ändere Namen und eliminiere Duplizierungen.

Ich verkleinere die Methoden und stelle sie um.... Manchmal breche ich ganze Klassen heraus…

Schließlich folgen meine Funktionen den Regeln… Ich schreibe sie nicht in dieser Form, wenn ich anfange. Ich glaube nicht, dass dies irgendjemand könnte.

Robert C. Martin, Clean Code, S. 81

39

Objekte und DatenstrukturenIst es ein lebendes Objekt oder ein Datenkonstrukt?

5.

Was ist hier der Unterschied?

public class Point {

public double x ;

public double y ;

}

public interface Point {

private double getX();

private double getY();

public void setCartesian(double x, double y);

private double getR();

private double getTheta();

public void set Polar (double r, double theta);

}

abgesehen davon, dass das Eine eine Klasse ist und das Andere ein Interface

41

Datenabstraktion

public class Point {

public double x;

public double y;

}

public interface Point {

private double getX();

private double getY();

public void setCartesian(double x, double y);

private double getR();

private double getTheta();

public void set Polar (double r, double theta);

}

Konkreter Punkt

◉ die Datenstruktur ist offengelegt

◉ selbst getter/setter würden nichts daran ändern!

Abstrakter Punkt

◉ die Implementierung ist verborgen◉ enthüllt abstraktes Interface, mit dem der Nutzer die

Essenz der Daten manipulieren kann◉ Daten können nur als Einheit manipuliert werden

(hier: Wie definiert man einen vollständigen Punkt?)

Implementierung

könnte intern als kartesischer Punkt implementiert sein, aber Polar-Koordinaten zur Verfügung stellen

oder umgekehrt!

42

Weiteres Beispiel

public interface Vehicle {

public double getFuelTankCapacityinGallons();

public double getGallonsOfGasoline();

}

public interface Vehicle {

public double getPercentFuelRemaining();

}

Konkreter Vehicle Tank

◉ über public ist interne Datenstruktur offen gelegt

Abstrakter Vehicle Tank

◉ Daten werden abstrahiert auf einen Prozentwert (Person will eine Vorstellung haben wie voll der Tank ist → Prozent gibt eine bessere Idee des Füllstandes)

43

Objekte verbergen ihre Daten hinter Abstraktionen und enthüllenden Funktionen

Der Unterschied zwischen Objekten und Datenstrukturen

Datenstrukturen enthüllen ihre Daten und haben keine Funktion

KOMPLEMENTÄR

ABSTRAKT

KONKRET

44

… die Vor- und Nachteile beider Typen prozedurales Beispielobjektorientiertes Beispiel

public class Square implements Shape {

private Point topleft ;private double side ;

public double area( ) {return side * side ;

}}

public class Rectangle implements Shape {...

◉ leicht neues Objekt (hier Shapes) hinzuzufügen

◉ schwer neue Funktionen hinzuzufügen (hier zB umfang() ) → alle Klassen müssen angefasst werden

public class Square {public Point top left ;public double side ;

}public class Rectangle { …

public class Geometry {public double area (Object shape) {

if (shape instanceof Square) {Square s = (Square) shape ;return s.side * s.side ;

} else if (shape instanceof Rectangle) { ...

enthüllende Funktion des Objekts

◉ leicht neue Funktionen hinzuzufügen (Shape Klassen müssen nicht verändert werden)

◉ schwer neue Datenstruktur (hier Shapes) hinzuzufügen → alle Funktionen müssen erweitert werden ( zB: else if (shape instanceof circle) { … )

Datenstrukturen

Verhalten separat

45

Konsequenz

◉ alles was für Datenstrukturen leicht ist, ist für Objekte schwer

◉ … und umgekehrt!

es gibt nicht nur die eine Wahrheit !

(zB alle objektorientiert ohne Kompromisse)

◉ man sollte sich für den jeweiligen Fall das am besten passende Konzept aussuchen

46

Das schlimmste aus beiden Welten

Objekt

Datenstruktur

Hybrid

◉ haben Funktionen, die wichtige Aufgaben erfüllen

◉ aber auch setter auf wichtige private Properties, die dazu verleiten die Objekt in prozeduraler Art zu verwenden

… Hybride

neue Funktionen hinzuzufügen ist schwer

0neue Datenstrukturen/Objekte hinzuzufügen ist schwer 47

Exkurs: Lombok ProjectClean Code ohne boilerplates

Das Project Lombok ist eine Java Bibliothek, die sich automatisch einklinkt in den Editor und die Build Tools.

Website des Projekts: https://projectlombok.org/gutes Tutorial: http://www.baeldung.com/intro-to-project-lombok

48

public class Language {

private int id;

private String name;

public Language(String name, int id) {

this.name = name;

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public int hashCode() {

// vieelll und langer Code hier ....

}

public String toString() {

// vieelll und langer Code hier ....

}

}

klassisches POJO gleiche POJO mit Lombok

@Getter @Setter@EqualsAndHashCode@ToString@RequiredArgsConstructor

public class Language {private int id;private String name;

}

geht das nicht noch kürzer?

Voilá!

@Data

public class Language {private int id;private String name;

}

Natürlich!

49

Vorteile von Lombok

◉ kein boilerplate Code mehr → Clean Code

◉ bei Änderungen der Objekt Properties muss man sich um nichts kümmern

◉ Annotationen können zu allen erdenklichen Sonderfällen konfiguriert werden

◉ Dinge die automatisiert werden können:

◉ Getter / Setter / Konstruktoren (auch für Listen)

◉ ToString() / Equals() / hashCode()

◉ Builder Pattern

◉ “sinnlose” Checked Exceptions catching

◉ AutoClose (ähnlich try with resources)

◉ Konfiguration von verschiedenen Logger

◉ @Synchronized für Thread-Safer Methods

◉ Objekt Komposition mit Delegation 50

Fehler- Handling… darf nicht die Logik verschleiern

6.

Wie viele Zeilen sind tatsächliche Geschäftslogik? 5 !

public class Device Controller { public void sendShutDown() { DeviceHandle handle = getHandle (DEV1); // Check the state of the device if (handle ! = DeviceHandle.INVALID) { // Save the device status to the record field retrieveDeviceRecord (handle) ; // If not suspended, shutdown if (record.getStatus() ! = DEVICE_SUSPENDED) { pauseDevice(handle) ; clearDeviceWorkQueue (handle) ; closeDevice(handle); } else { logger.log( "Device suspended"); } } else { logger.log("Invalid handle for: " + DEV1.toString()); } } ...

public class Device Controller { public void sendShutDown() { try { tryToShutDown() ; } catch (DeviceShutDownError e) { logger.log(e) ; } }...

private void tryToShutDown() throws DeviceShutDownError { DeviceHandle handle = getHandle(DEV1); retrieveDeviceRecord (handle) ; pauseDevice(handle) ; clearDeviceWorkQueue (handle) ; closeDevice(handle);}

private DeviceHandle getHandle(DeviceID id) { throw new DeviceShutDownError("Invalid handle for: " + f id.toString());}

verschleierte Geschäftslogik Geschäftslogik getrennt von Fehlerhandling 52

Vorteile

◉ Geschäftslogik ist klar erkennbar und getrennt

◉ Fehlerbehandlung findet separat statt

◉ weniger if-else Anweisung → verringert Komplexität des Codes

53

Weniger Ausnahmen

54

try {MealExpenses expenses = ffffffffexpenseReportDAO.getMeals(employee.getID());m_total += expenses.getTotal() ;

} catch (MealExpensesNotFound e) {m_total += getMealPerDay() ;

}

wenn Mitarbeiter kein Mahlzeit-Ausgabe eingereicht hat => DAO wirft Ausnahme⇒ dann wird Standard Tagessatz genommen

besserer Ansatz ohne Exception:

public class PerDayMealExpenses implements MealExpenses {public int getTotal() {

// Standard Tagessatz zurückgeben}

}

MealExpenses expenses = ffffffffexpenseReportDAO.getMeals(employee.getID());m_total += expenses.getTotal() ;

Reine Geschäftslogik ohne Ausnahmebehandlung

wenn DAO kein Employee MealExpenses findet wird Standard MealExpenses zurückgegeben

Kein NULL zurückgeben

55

List<Employee> employees = getEmployees() ;if (employees != null) {

for (Employee e : employees) {totalPay += e.getPay() ;

}}

Warum erlauben wir, dass getEmployees() auch null zurückgeben darf?

besserer Ansatz ohne NULL:

public List<Employee> getEmployees () {if ( ... keine Mitarbeiter vorhanden sind ... )

return Collections.emptylist();}

List<Employee> employees = getEmployees() ;for (Employee e : employees) {

totalPay += e.getPay() ;}

Keine fehleranfälligen NULL Abfragen mehr

wenn die Liste leer ist, geben wir einfache eine immutable emptylist aus Java Collections zurück

Kein NULL übergeben

56

NULL an Methode zu übergeben ist noch schlimmer als NULL zurückzugeben!

Problem:

◉ NullPointerExceptions entstehen

Lösung (aus Sicht des Entwicklers der Methode):

◉ NULL als möglichen Übergabeparameter vermeiden und verbieten

◉ lieber extra Delegations-Methoden schreiben, wenn bei Sonderfall einzelne Methodenparameter mit NULL belegt werden müssten

schlechte Tests sind schlimmer als gar keine Tests!

Unit-Tests7.

Warum brauchen wir saubere Tests?

● Testcode erleichtert Verständnis Produktionscode

● gute Tests können späteres Patching erleichtern oder ersetzen

● spart Geld● sorgt für entspannteres

Programmieren

Saubere Tests: Lesbarkeit

● klar kompakt verständlich

● kurz fassen● 3 Teile einteilen● 1. Testdaten● 2. Testdatenmanipulation● 3. Überprüfen

Unsauberer Test!

Besser?

F.I.R.S.T

● Fast● Independent● Repeatable● Self-Validating● Timely

Fast

Der Test sollte schnell laufen

Independent

● Die Tests sollen nicht voneinander abhängen

● d.h. ein Test darf nicht die Bedingung für einen anderen sein!

● ein assert pro Test● ein Konzept pro Test

Beispiel

Repeatable

Der Test sollte in jeder Umgebung die gleichen Ergebnisse liefern!

Self-Validating

Der Test sollte einen booleschen Output haben!

Timely

Der Test sollte rechtzeitig geschrieben werden!

Test Driven Development:

1. Produktionscode erst schreiben wenn scheiternder Test dazu geschrieben

2. dieser Test sollte nicht mehr Code enthalten als für den scheiternden Test nötig ist

3. nur so viel Code wie nötig ist um den scheiternden Test zu bestehen

Fazit:

● trotz nur ankratzen des Themas klar geworden wie wichtig saubere Test sind → Testcode genauso wichtig wie Produktionscode

saubere Klassen für sauberen Code

Klassen8.

Klassen klein halten!

Klasse darf nicht zu viele Verantwortlichkeiten haben

Beispiel:

public class Version {

public int getMajorVersionNumber()

public int getMinorVersionNumber()

public int getBuildNumber()

}

public class LastFocused {

public Component getLastFocusedComponent()

public void setLastFocused(Component lastFocused)

}

Single Responsibility Prinzip:

Eine Klasse oder ein Modul sollte nur einen Grund zur Änderung haben!

Kohäsion:

● Klasse sollte kleine Anzahl Instanzvariablen haben

● Jede Methode min. eine dieser Variablen manipulieren

● je mehr Variablen Methode manipuliert desto Kohäsiver

Warum Kohäsion?

● wenn kleine Funktionen kann es zu Vervielfältigung Instanzvariablen kommen → werden nur von Teilmenge der Methoden verwendet

● große Klasse wieder in kleiner Klassen aufteilen um Kohäsion zu gewährleisten

● Code wird noch übersichtlicher durch ein besseres Ordnen der Klassen und Methoden!

Änderungen der Klasse vorher einplanen

● jede Änderung Risiko ganze System nicht mehr funktioniert!

● Risiko möglichst gering halten!

● → Änderungen isolieren!

Änderungen einplanen!

● wenn verändern Klasse verändern

● Fehlerrisiko !!● → ganze Klasse

muss neu getestet werden

Änderungen einplanen!

● in Klassen isoliert!● Funktionen beschädigen

sich nicht mehr gegenseitig

● Änderungen, Testen einfacher

Isolation 1

● Änderungen in die konkreten KLassen ausgelagert

● keine Fehler in anderen Klassen bei Veränderung!

Isolation 2:

● trotz ständiger Änderung der Werte Test jetzt mög!

NebenläufigkeitDie Entkopplung des Was und Wann

9.

public class Id {private int lastIdUsed = 0;public int getNextId() {

return ++lastIdUsed;}

}

Nebenläufigkeit ist komplex

3 mögliche Antworten:

1. T1: 1 T2: 22. T1: 2 T2: 13. T1: 1 T2: 1

2 Threads -> 12,870 mögliche Ausführungspfade

zu beachten

- Bugs sind kaum reproduzierbar- Änderung der Design-Strategie- zusätzlicher komplexer Code- kenne deine Libraries

- java.util.concurrent

SRP

- Nebenläufigen Code von anderen Code trennen

defensive Nebenläufigkeit Programmierung

Daten Gültigkeitsbereich beschränken

- mit read-only Objekten arbeiten / Kopien arbeiten

- synchronized- synchronisierte

Abschnitte klein halten

Wenn Sie nebenläufigen Code schreiben, sollte Sie jeden Thread

möglichst so schreiben, als existiere er in seiner eigenen

Welt.

Brett L. Schuchert, Clean Code, S222

- Datenquellen isolieren- Daten in Variablen speichern- mit den Variablen arbeiten

unabhängige Threads

Erzeuger-Verbraucher, Leser-Schreiber, Philosophenproblem

Ausführungsmodelle kennen

Definitionen

Bound Resources

Speicherplatz ist endlich, Listen, Queues, Lese/Schreib-Puffer haben feste Größen

Starvation

Threads mit hoher Priorität werden zuerst bedient und verzögern, verhindern andere Threads

Livelock

Threads stehen sich im Weg / behindern sich bei der Arbeit

Mutual-Exclusion

Auf synchronisierte Datenquellen kann nur ein Thread gleichzeitig zugreifen

Deadlock

Thread A & B haben Ressourcen die der andere zum weiterarbeiten braucht, beide warten auf das Ende des anderen

Erzeuger-Verbraucher

Erzeuger Threads

E-T1 E-T2...

Verbraucher Threads

V-T1 V-T2...

Puffer

Objekt Objekt Objekt

Erzeuger-Verbraucher Problem

- Die Datenstruktur ist eine Bound Resource

- inkonsistenter Zustand der Datenstruktur

- Signalaustausch- Wartezeiten

- Queue ist voll- Queue ist leer

Deadlock

- gesicherte Datenstruktur- Semaphore

Probleme Lösungsansatz

Leser-Schreiber

Leser Threads

E-T1 E-T2...

Schreiber Threads

V-T1 V-T2...

Daten

Leser-Schreiber Problem

- Datenkonsistenz- Dauerschreiber

- kein Durchsatz- Dauerleser

- Daten nicht aktuell

- keine Leser, Schreiber schreiben

- Semaphore

Probleme LösungsansatzMutual-ExclusionStarvation

pluggable Code, Instrumentierung

Nebenläufigen Code testen

- Anderer Code sollte vorher Laufen- gelegentliche/einmalige Fehler <- potenzielle Threading Fehler- auf verschiedenen Betriebssystemen ausführen- Code pluggable schreiben- Code instrumentieren

- Früh auf Tools einigen

Threaded-Code testen

pluggable Code

- Anzahl der Threads variieren- Executer Framework

- mit mehr Threads als Prozessoren ausführen- Anzahl von Iterationen verändern

manuelle Instrumentierung

Manuelles einfügen von

- Object.wait()- Object.sleep()- Object.yield()- Object.priority()

Nadel im Heuhaufen

Code könnte übernommen werden

Jede Ausführung neues einfügen und entfernen

Einzelfall Überprüfung

automatisierte Instrumentierung

public class ThreadJigglePoint {

public static void jiggle() throws Exception{ double condition = Math.random() * 4; if(condition < 1){ long sleepingTime = (long)(Math.random() * 1000); Thread.sleep(sleepingTime); } else if(condition < 2) { Thread.yield(); } else if(condition < 3) { int priority =

((int)(Math.round(Math.random()*9+1))); Thread.currentThread().setPriority(priority); } else { //do nothing } }}

Bytecode manipulieren

- CGLIB (github.com/cglib/cglib)

- ASM (asm.ow2.io/)

eigene Klasse schreiben

Zusammenfassung

◉ Kommentare: kurz, prägnant und sinnvoll einsetzen

◉ Namen: gute Namen helfen, den Code zu verstehen

◉ Funktionen: haben “nur” eine Aufgabe. Alle Funktionen zusammen erzählen eine Geschichte

◉ Objekte & Datenstrukturen: entweder ... oder, Hybride führen zu Problemen

◉ Fehler-Handling: trenne Geschäftslogik vom Fehler-Handling und vermeide NULL

◉ Unit-Test: F.I.R.S.T , Lesbarkeit

◉ Klassen: Klassen klein halten, Änderungen isolieren

◉ Nebenläufigkeit: Nebenläufigen Code von anderen Code trennen, Datenquellen isolieren

Danke!

100

Quellen

Literatur

http://www.thejavageek.com/2015/04/10/dont-repeat-yourself-principle/

https://projectlombok.org/

http://www.baeldung.com/intro-to-project-lombok

http://clean-code-developer.de/

http://www.sebastianviereck.de/clean-code-richtige-und-falsche-kommentare/

http://software.jensgeyer.com/code/comments_are_bad.html

https://jaxenter.de/das-fuenfsekundenexperiment-guter-code-schlechter-code-33196

Bilder

https://dawidarte.deviantart.com/art/The-Riddler-502255335101