Suche
Schöner Coden - Teil 4

Experten-Check: Was ist „schöner Code“ für Java-Entwickler?

Redaktion JAXenter

© shutterstock.com / patpitchaya

In unserem Themen-Schwerpunkt “Schöner Coden” wollen wir uns der Frage stellen: Was ist eigentlich schöner Code? Kann man die „Schönheit“ von Programmcode auf eine bestimmte Eigenschaft reduzieren? Hilft ein bestimmtes Tool oder Framework dabei, schöneren Code zu schreiben? Im vierten Experten-Check beantworten uns die Java-Core-Experten Christian Kaltepoth, Uwe Schindler und Christoph Engelbert diese Fragen.

Was hilft dir (technisch) beim schöner Coden? Welche Tools/Frameworks machen Code schön?

Christoph Engelbert: Generell jede Form von Code Formatter. Ich bin immer der Meinung, dass ein Team einen gemeinsamen Formatter verwenden sollte. Dabei ist egal welcher, Hauptsache der Code ist danach einheitlich. Ansonsten bin ich momentan auf dem Kotlin-Trip. Die Sprache ist nah genug an Java dran um einen schnellen Einstieg zu haben, trotzdem prägnant, aber lesbar – nicht so wie mancher Scala-Code. Als letztes natürlich entsprechende Design Pattern. Aber bitte nicht mit Gewalt versuchen, jedes Muster irgendwie, irgendwo einzubauen.

Bitte nicht mit Gewalt versuchen, jedes Muster irgendwie, irgendwo einzubauen.

Christian Kaltepoth: Ganz wesentlich für die Lesbarkeit des Codes ist natürlich zunächst die Ausdrucksstärke der verwendeten Programmiersprache. Mit der Einführung von Lambdas und Streams hat Java 8 beispielsweise einen ganz deutlichen Schritt in diese Richtung gemacht. Warum Lambdas und Streams den Code schöner und lesbarer machen, lässt sich besonders gut an einem einfachen Beispiel demonstrieren. Nehmen wir einmal an, wir benötigen die Adressen unserer deutschsprachigen Kunden, sortiert nach deren Umsatz. Mit Java 7 würde man eine solche Anforderung typischerweise wie folgt implementieren.

List liste = new ArrayList<>();
 
for( Kunde kunde : kunden ) {
  if( kunde.getLand().equals( "DE" ) ) {
	liste.add( kunde );
  }
}
 
liste.sort( new Comparator() {
  @Override
  public int compare( Kunde k1, Kunde k2 ) {
	return Double.compare( k2.getUmsatz(), k1.getUmsatz() );
  }
} );
 
List adressen = new ArrayList<>();
for( Kunde kunde : liste ) {
  adressen.add( kunde.getAdresse() );
}
 

Hier sieht man, dass eine temporäre Variable für ein Zwischenergebnis benötigt wird. Solche Variablen haben aber einen Nachteil. Je mehr temporäre Variablen im aktuellen Scope definiert sind, desto schwieriger wird es, den Code zu verstehen. Denn man muss diese und deren aktuelle Werte immer im Hinterkopf behalten.

Dieser Code lässt sich mit Hilfe von Java 8 deutlich eleganter schreiben:

List<Adresse> adressen = kunden.stream()
  .filter( kunde -> kunde.getLand().equals( "DE" ) )
  .sorted( ( k1, k2 ) -> Double.compare( k2.getUmsatz(), k1.getUmsatz() ) )
  .map( kunde -> kunde.getAdresse() )
  .collect( Collectors.toList() );

Ich denke, dieser Vergleich zeigt sehr schön, wie der Einsatz moderner Sprach-Features zu besserem Code führen kann. Statt aktiv über Elemente zu iterieren und temporäre Variablen zu nutzen, beschreibt man auf eine fast deklarative Art und Weise, was mit den Elementen der Liste passieren soll.

Natürlich gibt es auch einige Bibliotheken, die helfen können, schöneren Code zu schreiben. Als Erstes wären hier die Bibliotheken zu erwähnen, welche häufig benötigte Funktionalität enthalten, die man sonst sehr umständlich selbst implementieren müsste. Beliebte Vertreter in dieser Kategorie sind beispielsweise Google Guava und Apache Commons Lang. Geht es beispielsweise darum, einen InputStream in ein Byte-Array zu konvertieren oder um häufig benötigte Funktionalität bei der Verarbeitung von Zeichenketten, so findet man in diesen Bibliotheken schnell, was man braucht. Besonders im Falle von Guava merkt man schnell, dass die Autoren sehr viel Wert auf ein gut lesbares API gelegt haben. So beispielsweise bei der Klasse Splitter:

List result = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split("some, words,, separated, by ,commas");

Hier muss man das API nicht lange erklären. Wie es verwendet wird, sieht man auf den ersten Blick.

Darüber hinaus sind Tools sehr hilfreich, die bei der Einhaltung von Code Conventions unterstützen. Denn was helfen solche Konventionen, wenn sie wissentlich oder unwissentlich nicht eingehalten werden. In diesem Bereich kann ich SonarQube sehr empfehlen. Mit Hilfe von SonarQube ist man sehr gut in der Lage, die Einhaltung von Konventionen zu überwachen und Bad Practices zu erkennen. Darüber hinaus erhält man genauen Einblick in diverse Code-Metriken und findet oft auch den ein oder anderen Bug im Code, der bisher nicht aufgefallen ist.

Es gibt nicht das generelle Tool für schönen Code.

Uwe Schindler: Ich denke, das hängt sehr von der Sichtweise ab, was denn überhaupt “schöner Code” ist. Mir persönlich ist in vielen Fällen egal, wie der Code optisch aussieht, ich achte mehr darauf, dass grundlegende Programmier-Pattern nicht verletzt werden und der Code vor allem gut lesbar ist. Dafür müssen Code-Standards benutzt werden – das empfinde ich dann als schön. Wunderschön wird es, wenn zusätzlich eine gewisse Eleganz in den Codeteil reinkommt.

Es gibt nicht das generelle Tool für schönen Code. Unter anderem macht das bloße Umformatieren oder einfaches Refactoring des Code mit Eclipse diesen nicht schöner: Die tolle Funktion in Eclipse, mit der alle Functional Interfaces im Code in Lambdas übersetzt werden, ist weit davon entfernt! Ich denke, der Entwickler sollte sich in jedem Fall nach dem ersten “runterprogrammieren” den Code ein weiteres Mal ansehen, und dann im zweiten Durchgang anfangen, unschöne Codeteile zu entfernen oder diese völlig neu zu schreiben. Code wird immer besser, wenn er noch einmal neu geschrieben wird. Auch ist nicht jeder Hype gut für das aktuelle Codeproblem. Nur weil jetzt jeder erzählt, dass man in Java Lambdas benutzen soll und das eben cool und schön ist, heißt das nicht, dass das gut ist. Es gab ja mehrere Artikel schon darüber, das darunter die Performanz leidet, aber auch die Lesbarkeit.

Ich persönlich schalte in Eclipse die automatische Formatierung des Code als aller erstes ab.

Generell schalte ich persönlich in Eclipse die automatische Formatierung des Code als aller erstes ab. Syntaxvervollständigung ist gut, aber die Entwicklungsumgebung hat mir gefälligst nicht vorzuschreiben, wann ich eine neue Zeile einfüge oder wie ich meine Klammern setze. Ich verstehe meinen Code selbst auch als künstlerische Arbeit, daher möchte ich bestimmen, wie er auszusehen hat. Und das mache ich per Hand: Nachdem schnell etwas zusammengestrickt wurde, spende ich normalerweise noch die dreifache Zeit und schlaflose Nächte, um es elegant und/oder standardkonform auszudrücken. Das erleichtert dann die Wartung!

Es gibt aber in der Tat Tools, die Code schöner machen können. Hierunter verstehe ich Tools, die mich auf (mögliche) Fehler hinweisen. Dazu gehören die Open-Source-Projekte wie CheckStyle, aber auch andere statische Analyse-Tools wie PMD. Allerdings sollte der Entwickler auch hier wissen, was er von diesen Tools erwarten kann.

Mir persönlich am Herzen liegen Flüchtigkeitsfehler, gerade aus der Internationalisierung, oder Probleme, die durch Defaults im System hervorgerufen werden, auf die der Entwickler keinen Einfluß hat. Daher achte ich generell bei Code Reviews immer darauf, dass an allen Stellen, wo Dateien geöffnet werden, auch explizit ein Charset angegeben wird. Oder wenn eine Fehlermeldung explizit in Englisch ausgegeben wird, dass der zugehörige Formatter auch Locale.ENGLISH benutzt. Das gehört zu korrektem Code dazu und zeigt dem Reviewer, dass der Entwickler die Probleme kennt und verstanden hat. Als Tool kann ich da “Forbidden-APIs” empfehlen, ein Maven/Gradle/Ant-Plug-in, was ich selbst entwickelt habe und genau solche Fehler vermeidet. Es wird bereits in vielen Open-Source-Projekten eingesetzt.

Bei welchem Fehler rollen sich bei dir die Fußnägel hoch? Was ist schlechter Code?

Christoph Engelbert: Jede Form von Tri-State Boolean. Es gab mal Zeiten, als ich mit einer FuzzyBoolean-Klasse arbeiten musste, weil das viel besser ist als Boolean.TRUE, Boolean.FALSE und null 🙂 Ansonsten bin ich relative resistent gegen jede Form von schlechtem Code. Ich selber versuche, meine eigenen Projekte einfacher, sauber und verständlich zu halten. Muss ich mit Fremdcode arbeiten, geht das fast immer, zumindest für eine Weile.

Christian Kaltepoth: Da ich sehr viel Wert auf lesbaren und intuitiv verständlichen Code lege, verwundert es vermutlich nicht weiter, dass ich unordentlichen und unstrukturierten Code besonders schlimm finde. Ein Klassiker des unordentlichen Codes ist für mich beispielsweise inkonsistente Einrückung, besonders bei XML- oder HTML-Dokumenten. Wenn man sich bei jedem schließenden HTML-Element fragen muss, wo denn das entsprechende öffnende Element zu finden ist, dann läuft etwas grundsätzlich falsch. Nur bei durchgängig korrekter Einrückung ist die hierarchische Struktur des Dokuments einfach zu verstehen und nur so ist direkt klar, in welcher Verschachtelungsebene man sich gerade befindet.

Einbuchstabigen Bezeichner von Variablen sind für mich ein rotes Tuch.

Auch die berühmten einbuchstabigen Bezeichner von Variablen sind für mich ein rotes Tuch. In jeder Zeile des Quellcodes fragt man sich dann, was sich denn noch gleich hinter den Bezeichnern b, za oder u verbirgt. Hier die Übersicht zu bewahren, ist nahezu unmöglich. Die einzige Ausnahme, bei der aus meiner Sicht auch einbuchstabige Bezeichner zu rechtfertigen sind, sind einfache Lambda-Ausdrücke und der berühmte Zählindex i in for-Schleifen. Ansonsten sollte man Variablen stets so benennen, dass auf Anhieb klar ist, worum es sich handelt.

Diese Fälle von unsauberen Code finde ich besonders deshalb so schlimm, weil es hier um die absoluten Grundlagen der professionellen Softwareentwicklung handelt. Und jede moderne IDE kann schließlich mit einem Tastendruck Quellcode formatieren oder eine Variable umbenennen. Oft fehlt es hier an dem Bewusstsein dafür, dass solch schlechter Code langfristig immer zu Problemen führt. Sei es durch hohen Wartungsaufwand oder durch aufwendiger Einarbeitung neuer Entwickler.

An dieser Stelle lohnt es sich auch noch zu erwähnen, dass es sich stets lohnt, unsauberen Code zu verbessern. Natürlich ist es oft verlockend, unsauberen und umstrukturieren Code einfach wegzuwerfen und den entsprechenden Teil des Systems neu zu schreiben. Aber selbst im schlimmsten Code verstecken sich oft noch Details und Sonderfälle, die nicht auf den ersten Blick ersichtlich sind und wertvolles Wissen darstellen. Daher ist es oft zu empfehlen, den alten Code Schritt für Schritt zu verbessern und aufzuräumen. Man könnte zunächst beispielsweise Variablen, Methoden und Klassen klarer benennen um dann im nächsten Schritt mit einigen Extract Method Refactorings mehr Struktur in den Code zu bringen. Glücklicherweise sind IDEs in der Lage, solche Prozesse maximal zu unterstützen und dabei sicherzustellen, dass man die Programmlogik nicht unabsichtlich verändert. So kann man Schritt für Schritt die Qualität des Codes verbessern, bis es zu dem Punkt kommt, bei dem das Lesen des Codes wieder Spaß macht.

Uwe Schindler: Der schlechteste Code, den ich je gesehen habe, wird automatisch durch Eclipse erzeugt, wenn man es nicht abstellt:

    try {
      Path file = Paths.get(“...”);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

Das widerspricht jeglicher guten Praxis:

  1. Exceptions sollten nicht verschluckt werden, sondern korrekt behandelt werden – im Normalfall am einfachsten via throws-Clause zum Aufrufer durchgereicht werden.
  2. Code sollte niemals einfach etwas auf die Konsole (System.out) ausgeben, dafür sind Logger da!

Ich bringe dieses Beispiel hier, weil man dieses Codeschnipsel wirklich überall sieht. Die Suche auf Google oder direkt in GitHub bringt Millionen von Ergebnissen: Leute schreiben irgendwelchen Code, dann meckert der Compiler, dass da eine Exception nicht abgefangen wird, und statt dann zum Aufrufer der Methode durchzureichen, wird einfach mit rechter Maus geklickt und das denkbar unsinnigste Quick Fix in Eclipse ausgewählt. Ich habe dies sogar in Handouts von Uni-Vorlesungen gesehen, ich finde das unfassbar!

Wenn du eine Eigenschaft nennen müsstest, die für dich schönen Code ausmacht, welche wäre das und warum?

Christoph Engelbert: Zählt Empathie als eine dieser Eigenschaften? Ich denke, als Entwickler muss man immer arbeiten, als ob man nicht alleine an einem Projekt sitzt – meistens ist es ja auch so. Ich muss mich also in die Lage versetzten könne, dass jemand meinen Code verstehen muss, jemand, der ihn nicht geschrieben hat. Ganz abgesehen davon, dass es oft genug passiert, dass man seinen eigenen Code nach ein paar Wochen nicht mehr versteht. Gerade wieder so einen Fall bei meiner eigenen Query-Language gehabt, ergo nicht intuitiv, weg damit und neue Idee her :-).

Für mich ist Code schön, wenn es Spaß macht, ihn zu lesen.

Christian Kaltepoth: Für mich ist Code schön, wenn es Spaß macht, ihn zu lesen. Dafür muss der Code vor allem intuitiv verständlich sein. Denn nichts ist anstrengender, als unsauberen und schwer nachvollziehbaren Code zu lesen. Man muss sich stets bewusst sein, dass Quellcode deutlich häufiger gelesen als geschrieben wird. Gerade deshalb sollte der Code für das Lesen optimiert sein. Ist das nicht der Fall, treibt es schnell den Wartungsaufwand in die Höhe. Um beispielsweise einen Bug in einem Legacy-System zu beheben, muss man sich möglichst schnell und einfach in den Quellcode hineindenken können. Und das schafft man nur, wenn der Code in einer leicht nachvollziehbaren Art und Weise geschrieben ist.

Es gibt natürlich eine ganze Reihe von Eigenschaften, die Code leichter verständlich machen. Ein erster wichtiger Schritt für schönen Code ist sicherlich die Einführung von Code Conventions. Klare Regeln für das Formatieren des Quellcodes sorgen für ein einheitliches Bild und erleichtern das Lesen des Codes deutlich. Wie die Regeln genau aussehen, ist dabei gar nicht so wichtig. Ob das Team jetzt Tabs oder Spaces nutzen möchte oder ob der Ternary-Operator erlaubt ist oder nicht, sei dem Team selbst überlassen. Wichtig ist nur, dass sich alle daran halten. Trotzdem empfehle ich einen Blick in die Google Code Conventions für Java und den Airbnb JavaScript Style Guide, da diese viele gute Empfehlungen beinhalten.

Darüber hinaus ist es für die Verständlichkeit des Quellcodes äußerst wichtig, dass der Code sich auf das Wesentliche reduziert und nicht unnötig aufgebläht ist. Natürlich lässt sich Komplexität nicht immer vermeiden, jedoch neigen wir Softwareentwickler häufig dazu, Konzept unnötig zu verkomplizieren. Man sollte stets versuchen, sich an die Prinzipien KISS (Keep it small and simple) und YAGNI (You Ain’t Gonna Need It) zu halten und nicht für jede kleine Anforderung eine extrem generische Lösung suchen. Manchmal sind Lösungen einfach deutlich einfacher, als man es zunächst glaubt. Und einfache Lösungen sind natürlich auch deutlich einfacher zu verstehen.

Wichtig ist es auch, sich stets an das Single-Responsibility-Prinzip (SRP) zu halten. Jede Klasse sollte stets nur eine fest definierte Aufgabe zu erfüllen haben. Hält man sich nicht an diese Regel, entstehen schnell God Classes, die immer größer werden und kaum mehr zu überblicken sind. Es ist zugegebenermaßen nicht immer leicht, sich an dieses Prinzip zu halten, aber auf lange Sicht rentiert es sich dann doch, da der Code übersichtlicher und somit wartbarer wird.

Methoden sollten klein und übersichtlich bleiben.

In einer gewissen Weise lässt sich das Single-Responsibility-Prinzip natürlich auch auf Methoden übertragen. Auch hier gilt, dass Methoden klein und übersichtlich bleiben sollten. Außerdem sollte stets darauf geachtet werden, dass Methoden auf einem Abstraktionslevel bleiben. Eine Methode sollte also nicht gleichzeitig Geschäftsregeln und komplexe String-Manipulationen implementieren. Hier gilt es tiefere Abstraktionslevel an weitere Methoden zu delegieren. Das klingt vielleicht aufwendig, ist aber mit ein paar Extract Method Refactorings schnell durchgeführt.

Ein weiterer guter Tipp für besseren Quellcode ist es, möglichst auf Kommentare zu verzichten. Das klingt vielleicht zunächst widersprüchlich, weil Kommentare dem Leser des Codes helfen sollen, ihn zu verstehen. Versucht man aber auf Kommentare zu verzichten und stattdessen den Code so zu schreiben, dass man ihn auch ohne Kommentare versteht, entsteht automatisch sauberer Code. Hat man als Entwickler normalerweise das Gefühl, dass etwas dem Leser nicht sofort klar ist, sollte man also nicht einen Kommentar mit einer Erklärung hinzufügen, sondern stattdessen den Code umbauen, sodass man ihn auch ohne Kommentar versteht. Damit vermeidet man auch direkt das Problem von falschen Kommentaren, die in die Irre führen. Ich habe in einem Projekt noch kürzlich den folgenden Code gefunden:

// Reminder emails are sent after 3 days
private static final int THRESHOLD = 5;

Hier hat der ursprüngliche Entwickler in guter Absicht die Konstante kommentiert. Später hat dann jemand vermutlich den Wert der Konstante angepasst, aber vergessen den Kommentar ebenfalls zu ändern. Sinnvoller wäre es hier natürlich gewesen, die Konstante beispielsweise in EMAIL_REMINDER_THRESHOLD_DAYS umzubenennen. Dann wäre der Kommentar überflüssig gewesen.

Uwe Schindler: Ich nenne nur ein paar Stichwörter: Wiederverwendbar, gut strukturiert und einfach zu verstehen, aber keine unnötigen Abstraktionen.

shutterstock_107815670Schöner Coden
In unserer Reihe zum Thema „Schöner Coden“ stellen wir Experten verschiedener Disziplinen die Frage: Was ist eigentlich schöner Code? Welche Eigenschaft hat schöner Code aus der Sicht eines Software-Architekten? Und wie blicken Tester auf dieses Thema? Hier finden Sie eine Übersicht der bisher veröffentlichten Experten-Checks:

.

Die Java-Core-Experten

engelbert Christoph Engelbert ist der Technical Evangelist und Senior Solutions Architect bei Hazelcast. Außerdem ist er Apache Committer und Open-Source-Enthusiast. Er interessiert sich für Performance-Optimierungen und alles rund um die Internals der JVM und den Garbage Collector.

Christian Kaltepoth Christian Kaltepoth arbeitet als Senior Developer bei ingenit GmbH & Co. KG in Dortmund. Sein Schwerpunkt ist die Entwicklung von webbasierten Unternehmens -anwendungen auf Basis von Java-EE-Technologien. Darüber hinaus ist er in mehreren Open-Source-Projekten wie Apache DeltaSpike, Rewrite, PrettyFaces und Togglz aktiv. Er ist Mitglied der JSR 371 Expert Group und ein regelmäßiger Sprecher auf Konferenzen.

Uwe Schinlder Uwe Schindler ist Mitglied des Project Management Committee im Apache-Lucene-Projekt. Er ist mit seiner Consulting-Firma SD DataSolutions GmbH in Bremen ansässig und kümmert sich am Zentrum für Marine Umweltwissenschaften (MARUM) um die Suche nach geowissenschaftlichen Daten in der Umweltdatenbank PANGAEA.

Geschrieben von
Kommentare

Hinterlasse einen Kommentar

1 Kommentar auf "Experten-Check: Was ist „schöner Code“ für Java-Entwickler?"

avatar
400
  Subscribe  
Benachrichtige mich zu:
Peer
Gast

Unabhängig vom sonstigen Inhalt des Artikels, finde ich es erschreckend, dass selbst heute noch Experten Werte wie „Umsatz“, die ja gemeinhin Geldbeträge darstellen, als Double modellieren. (Auch wenn das hier im Kontext tatsächlich keine Rolle spielt!). Dies dürfte praktisch nie den Fachanforderungen genügen. Trotzdem wird es immer wieder in Beispielen und demzufolge wenig überraschenderweise auch in Echtcode, so gemacht.