Gute Zeilen schlechte Zeilen, JUG Ostfalen 29.8.2013
1. Gute Zeilen, schlechte Zeilen
Regeln für wartbare Programme
Java User Group Ostfalen, 29.08.2013
Dirk Weil, GEDOPLAN GmbH
2. Dirk Weil
GEDOPLAN GmbH, Bielefeld
Java EE seit 1998
Konzeption und
Realisierung
Vorträge
Seminare
Veröffentlichungen
2Gute Zeilen, schlechte Zeilen
4. Gibt es guten und schlechten Code?
Software ist (fast) nie fertig
Software wird (meist) im Team entwickelt
Teams ändern sich über die Zeit
Software muss verständlich sein
4Gute Zeilen, schlechte Zeilen
5. Gibt es guten und schlechten Code?
Entwicklerteams sind meist heterogen
Berufserfahrung
Programmierstil
…
Richtlinien helfen
bei der Einarbeitung in fremde Software
bei der (Weiter-) Entwicklung
5Gute Zeilen, schlechte Zeilen
7. Statische Code-Analyse
Matching des Codes gegen Regelsätze
Einfache (Text-)Pattern … strukturelle Pattern
7
Checkstyle
Gute Zeilen, schlechte Zeilen
8. Dokumentation
Javadoc für API
Klassen, Interfaces,
Methoden, Variablen
public, protected
Kontrolle bspw. per Checkstyle
Javadoc Comments
Prüft per Default auch private
scope = protected
Getter/Setter-Doku meist überflüssig
allowMissingPropertyJavadoc = true
8Gute Zeilen, schlechte Zeilen
9. Dokumentation
API-Dokumentation
veröffentlichen
Source-Jars erzeugen,
z. B. mit Maven
ermöglicht Unter-
stützung durch
die IDE
9
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<inherited>true</inherited>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
Gute Zeilen, schlechte Zeilen
10. Dokumentation
Erklärungsbedürftige Codesequenzen
Trivialdokumentation ist überflüssig
10
/*
* Fahrstrassen innerhalb von Fahrstrassen expandieren, d. h. durch ihre Elemente
* ersetzen. Dies geschieht in einer Schleife solange, bis alle Expansionen
* erledigt sind oder kein Fortschritt mehr erzielt wird.
*/
int letzteAnzahlFahrstrassenFahrstrassen = 0;
int anzahlFahrstrassenFahrstrassen = 0;
while (true)
{
for (Fahrstrasse fahrstrasse : this.fahrstrassen)
…
// Neue Weichenstellung protokollieren
this.logger.trace(this + ": setStellung(" + stellung + ")");
Gute Zeilen, schlechte Zeilen
11. Namen
Klassen, Variablen, Methoden entsprechend ihrer Aufgabe benennen
spart umfangreiche Dokumentation
Well-Known Names nicht umdeuten!
Anschauungsbeispiel: "Nothalt-Funktion"
Anlagenstatus ist zu generell
Methode setzt den Status nicht, sondern toggelt
setXyz ist well-known mit anderer Bedeutung
11
public void setAnlagenstatus()
{
Anlagenstatus.getInstance().changeAnlagenstatus();
Gute Zeilen, schlechte Zeilen
12. Namen
Keine Präfixnotation für Typen, Sichtbarkeit etc.:
Präfixnamen tendieren zur Unlesbarkeit
this. ist aussagekräftiger (OO) als m_
Unterscheidung Klasse vs. Interface zweitrangig
werden durch IDE-Unterstützung mehr als ersetzt
12
Instanzvariable Name beginnt mit m_
Variable vom Typ List<Integer> Name beginnt mit lI_
Interface Name beginnt mit I
… …
Gute Zeilen, schlechte Zeilen
13. Namen
CS kann Namenskonventionen prüfen
(Module group Naming Conventions)
IDE-Komfort nutzen!
Quick fix: Namensvorschläge (Variablen, Konstanten ..)
Save actions: Member mit this. qualifizieren
…
13Gute Zeilen, schlechte Zeilen
14. Formatierung
Code sollte im Team einheitlich formatiert sein
Einrückung (Tab/Blanks, wie viele?)
Platzierung von {
Zeilenumbruch
…
Vorteile
Code liest sich leichter
kleinere Change Sets im SCM
Lässt sich mit IDE-Komfort leicht erreichen
Save action: Format code
14Gute Zeilen, schlechte Zeilen
15. equals, hashCode
equals definieren hashCode definieren
nicht nur equals(MyType)
Codeanalyse:
CS: Equals and Hashcode, Covariant Equals
FB: Class defines equals and uses Object.hashCode
15Gute Zeilen, schlechte Zeilen
16. equals, hashCode
Jedes Geschäftsobjekt sollte equals und hashCode definieren
IDEs bieten gute Unterstützung
Achtung bei equals in Basisklassen
16
public class BadEquals
{
public boolean equals(Object obj)
{
…
if (getClass() != obj.getClass())
// if (!(obj instanceof BadEquals)) // unsymmetrisch, wenn Subklasse überschreibt
// if (obj.getClass() != BadEquals.class) // funktioniert nicht für Subklassen
{
…
Gute Zeilen, schlechte Zeilen
21. Komplexität
Klassen / Methoden nicht zu lang
Anzahl Methodenparameter nicht zu groß
CS: Maximum Method Length, Maximum Parameters,
Maximum File Length,
Cyclomatic Complexity
(Anwendung im
Team diskutieren!)
21Gute Zeilen, schlechte Zeilen
22. Einfach machen
Einfache Lösungen sind gute Lösungen
Vorsicht bei:
Reflection
extrem schlecht lesbar
Refactoring problematisch
hochgradig konfigurierbaren Klassen
schwer nutzbar (bspw. GridBagConstraints)
übermäßigem Einsatz von Typparametern
22Gute Zeilen, schlechte Zeilen
23. Einfach machen
Anschauungsbeispiel: Entity Editor
Generischer Editor für Geschäftsobjekte
Steuerung per Annotation @Editable
Remote funktionsfähig ( kein EntityManager)
Hochgradige Nutzung von Reflection
Umfangreiche Konfiguration von Services etc.
Einsatzfall BDE-System
ca. 35 Entities
23Gute Zeilen, schlechte Zeilen
24. Klassen sparen lohnt nicht
Anschauungsbeispiel "Wachdienst":
Gebäudekontrolle durch Prüfung aller Räume
Räume sind auf Etagen verteilt
Kontrollierte Räume
werden abgehakt
24Gute Zeilen, schlechte Zeilen
25. Klassen sparen lohnt nicht
Anschauungsbeispiel "Wachdienst"
1. Ansatz: Keine Klasse für Etage
Dialogaufbau unnötig kompliziert
(~ Gruppenwechsel)
Kein Platz für zukünftige Erweiterung
um Etagen-Daten
25Gute Zeilen, schlechte Zeilen
26. Klassen sparen lohnt nicht
Anschauungsbeispiel "Wachdienst"
Besser: Zusätzliche Ebene für Etagen
Klares Konzept
Real World Klasse
26Gute Zeilen, schlechte Zeilen
27. Wohin mit der Logik?
27Gute Zeilen, schlechte Zeilen
28. Wohin mit der Logik?
Anschauungsbeispiel "Modellbahnsteuerung":
Reservieren einer Fahrstraße
=Stellen der betroffenen
Weichen und Signale
Fahrstrasse liegt als
Geschäftsobjekt vor
Anforderung als Webservice
28Gute Zeilen, schlechte Zeilen
29. Wohin mit der Logik?
Anschauungsbeispiel "Modellbahnsteuerung":
1. Ansatz: Iteration über Fahrstraßenelemente im Webservice
Nachteile:
nicht wieder-
verwendbar, da
im Webservice
"Polymorphie
für Arme"
(instanceof
~ goto der OO)
29
@POST
@Path("/fahrstrasse/{bereich}/{name}/reserviert")
public Response setFahrstrassenreservierung(...)
{
Fahrstrasse fahrstrasse = …
for (FahrstrassenElement fe
: fahrstrasse.getElemente())
{
Fahrwegelement fwe = fe.getFahrwegelement();
if (fwe instanceof Weiche)
{
Weiche w = (Weiche) fwe;
w.setStellung(…);
}
…
Gute Zeilen, schlechte Zeilen
30. Wohin mit der Logik?
Anschauungsbeispiel "Modellbahnsteuerung":
Besser: Platzierung der Logik in Fahrstrasse,
abstrakte Methode statt expliziter Typabfrage
30
@Path("{bereich}/{name}/reserviert")
@POST
public Response setReserviert(…)
{
Fahrstrasse fahrstrasse = …
fahrstrasse.setReserviert(reserviert);
}
public void setReserviert(boolean reserviert)
{
for (FahrstrassenElement element : this.elemente)
element.setReserviert(reserviert);
public abstract void setReserviert(boolean reserviert);
Gute Zeilen, schlechte Zeilen
31. Wohin mit der Logik?
Geschäftslogik in Geschäftslogik-Schicht
CDI, EJB, JPA, …
Präsentationslogik bspw. in JSF-Beans
CDI-Models, Managed Beans
Boundary-Code in EJBs, Webservices etc.
Saubere Schichtung
erhöht Wiederverwendbarkeit
macht den Code übersichtlicher
31Gute Zeilen, schlechte Zeilen
32. In Objekten denken
Anschauungsbeispiel: 1:n-Relation (JPA)
Query "Suche Books eines Publishers"
(Warum so kompliziert?):
32
@Entity
public class Publisher
{
@Id @GeneratedValue Integer id;
@OneToMany(mappedBy = "publisher") List<Book> books;
…
@Entity
public class Book
{
@Id @GeneratedValue Integer id;
@ManyToOne Publisher publisher;
Publisher publisher = …;
… em.createQuery("select b from Book b where b.publisher.id=:publisherId", Book.class)
.setParameter("publisherId", publisher.getId())
.getResultList();
Gute Zeilen, schlechte Zeilen
34. Anekdoten
34
String name = new String();
name = "Hugo";
if (val != null && ("" + val.getClass().getName()).equals("java.lang.String"))
Set<String> texte = …;
Set<Object> labels = new TreeSet<>();
labels.addAll(texte);
int adr = …;
String adrAsString = new Integer(adr).toString();
Offensichtlich komplett falsche Vorstellung
von Objekten und Referenzen darauf
instanceof
Sind die Labels nicht Texte? Warum dann Set<Object>?
Wenn unterschiedliche Typen erlaubt: Set<?>
Integer.toString(adr)
Gute Zeilen, schlechte Zeilen
35. Anekdoten
35
public void changeRichtung()
{
if (isRueckwaerts())
setRueckwaerts(false);
else
setRueckwaerts(true);
}
public class Anlagenstatus
{
private static Anlagenstatus anlagenstatus = null;
private Anlagenstatus() { }
public static Anlagenstatus getInstance()
{
if (anlagenstatus == null)
anlagenstatus = new Anlagenstatus();
return anlagenstatus;
}
setRueckwaerts(!isRueckwaerts())
public static final Anlagenstatus
= new Anlagenstatus()
Noch besser: enum-Singleton
Gute Zeilen, schlechte Zeilen
36. Anekdoten
36
@POST
@Path("artikel/{artNr}/menge")
public Response setSpeed(@PathParam("artNr") String adrNr,
@FormParam("menge") String menge)
{
try
{
int mengeInt = Integer.parseInt(menge);
…
}
catch (NumberFormatException e)
{
Auto car1 = ...;
Auto car2 = ...;
if (car1.getId().getValue().equals(car2.getId().getValue()))
{
...
Gute Zeilen, schlechte Zeilen
int menge
if (car1.equals(car2))
38. Qualitäts-Schulden
Schlechte Code-Qualität = Kredit
Code-Review + Korrektur = Rückzahlung
Weiterentwicklung trotz Schwächen
= Zinszahlungen
können überwältigend werden!
Nichts tun = "absaufen"
Ad-hoc-Maßnahmen sind
keine Dauerlösung
38 Gute Zeilen, schlechte Zeilen
39. Mehr?
Seminare zum Thema Java SE und Java EE, z. B.
Java Effective
Power Workshop Java EE
http://ips-it-schulungen.de/Kurse/Java
Fragen!
dirk.weil@gedoplan.de
39Gute Zeilen, schlechte Zeilen