Clean Code - ziemers.de€¦ · - Convention over Configuration Was ist Clean Code? sichtbar...
-
Upload
nguyentruc -
Category
Documents
-
view
232 -
download
0
Transcript of Clean Code - ziemers.de€¦ · - Convention over Configuration Was ist Clean Code? sichtbar...
Gliederung
1. Einführung2. Kommentare3. Namen4. Funktionen5. Objekte und Datenstrukturen6. Fehler-Handling7. Unit-Tests8. Klassen9. Nebenläufigkeit
2
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
“
“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
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)
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;
}}
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
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)
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());
}
“
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
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
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
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
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
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
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!
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
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 Problem
- Datenkonsistenz- Dauerschreiber
- kein Durchsatz- Dauerleser
- Daten nicht aktuell
- keine Leser, Schreiber schreiben
- Semaphore
Probleme LösungsansatzMutual-ExclusionStarvation
- 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
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