Clean Code in Java, Teil 1

Clean Code bedeutet leicht lesbaren, wartbaren und veränderbaren Code. Es geht nicht um Perfektion, sondern um Klarheit der Absicht: Jede Klasse, Methode und Variable soll kommunizieren, was sie tut – ohne Kommentar, ohne Nachdenken.

Es führt dazu, dass man den Code einfacher testen kann, und mit guter Testabdeckung können wir den Code mit höherer Sicherheit erweitern.

Hier Bespiele von schlechte und bessere Praxis:

// Schlecht – Name verrät nichts über die Absicht
public void p() {}
// Besser – sofort verständlich
public void processOrder() {}

Lesbarkeit:

processOrder() kommuniziert den Zweck sofort. Kein mentaler Aufwand, kein Kommentar nötig.

Wartbarkeit:

Bei Bugfixes und Reviews ist der Kontext direkt klar. Die Suche nach „was macht p?“ entfällt.

Erweiterbarkeit:

Neue Entwickler können gezielt erweitern, ohne den gesamten Call-Graph zu verstehen.

Effective Naming:

// Schlecht
int d = 3650;
// Besser – Einheit im Namen
int ageInDays = 3650;
// Am besten
static final int TEN_YEARS_IN_DAYS = 3650;

Lesbarkeit

Einheit und Konzept sind direkt aus dem Namen ableitbar – kein Kommentar, kein Kontext nötig.

Wartbarkeit

Magic Numbers sind gefährlich. Ein benannter Wert zeigt, wo Änderungen wirken.

Erweiterbarkeit

Konstanten können zentral geändert werden. Umbenennung per IDE ist sicher und vollständig.

Abstraktion:

Richtige Abstraktionsebene vereinfacht das Verständnis

Eine Methode soll auf einer einzigen Abstraktionsebene operieren. placeOrder() delegiert – sie beschreibt was passiert, nicht wie.

public void placeOrder() {
validateOrder(); // hohe Abstraktion
persistOrder(); // hohe Abstraktion
notifyCustomer(); // hohe Abstraktion
}
// Schlechtes Gegenstück: Details mischen sich
public void placeOrder() {
if (order.items.isEmpty()) throw new IllegalStateException();
db.save(order);
smtp.send(customer.getEmail(), "Bestätigung...");
}

Lesbarkeit:

Drei Zeilen erzählen eine Geschichte. Der Leser versteht den Flow ohne Details zu kennen.

Wartbarkeit:

Jede Sub-Methode kann unabhängig geändert, getestet und ersetzt werden.

Erweiterbarkeit:

Ein neuer Schritt (z. B. reserveInventory()) wird einfach eingefügt, ohne die Logik anderer Schritte zu berühren.

Cognitive Load:

Refactoring reduziert mentale Belastung

Jede Bedingung, Verschachtelung und Variable erhöht die mentale Last. Prägnante Guards und sprechende Methoden entlasten das Arbeitsgedächtnis.

// Hoch – zwei Konzepte gleichzeitig prüfen
if (user != null && user.isActive()) { ... }
// Reduziert – Intention benannt, null-sicher
if (isActiveUser(user)) { ... }
private boolean isActiveUser(User user) {
return user != null && user.isActive();
}

Lesbarkeit:

Die if-Bedingung liest sich wie ein Satz. Kein Null-Check im Kopf halten nötig.

Wartbarkeit:

Null-Checks oder weitere Regeln werden an einer Stelle gepflegt. Keine vergessenen Duplikate.

Erweiterbarkeit:

Erweiterte Prüfungen (z. B. user.isVerified()) werden in isActiveUser() ergänzt.

Lean Code:

Nur das bauen, was aktuell benötigt wird

Code der nicht existiert, kann keine Bugs enthalten. InvoiceService soll genau die heute benötigten Features umsetzen – keine spekulativen Methoden, keine „vielleicht später“-Strukturen.

// Spekulativ – ungenutzte Infrastruktur
public class InvoiceService {
void generatePdf() {}
void exportToXml() {} // "könnte gebraucht werden"
void syncWithSAP() {} // niemand braucht es
}
// Lean – nur was gebraucht wird
public class InvoiceService {
void createInvoice(Order order) {}
}

Lesbarkeit:

Kleine Klassen sind vollständig im Kopf haltbar. Kein Rauschen durch ungenutzten Code.

Wartbarkeit:

Weniger Code = weniger Testaufwand, weniger Bugs, kürzere Reviews. Dead Code schleicht sich ein.

Erweiterbarkeit:

Neue Features werden dann hinzugefügt, wenn sie gebraucht werden – mit aktuellem Wissen über die Anforderungen.

Spezifischer vs. abstrakter Code:

Zu viel Abstraktion erhöht Komplexität

Interfaces sind mächtig – aber nur wenn sie echte Varianten abstrahieren. Ein PaymentProcessor-Interface ist sinnvoll, wenn mehrere Implementierungen (Stripe, PayPal, SEPA) existieren oder geplant sind.

// Nur eine Impl. existiert → Interface ist Overengineering
interface PaymentProcessor {}
class StripePaymentProcessor implements PaymentProcessor {}
// Passend – echte Varianten rechtfertigen das Interface
interface PaymentProcessor {
PaymentResult process(Payment payment);
}
class StripeProcessor implements PaymentProcessor { ... }
class PayPalProcessor implements PaymentProcessor { ... }

Lesbarkeit:

Konkrete Klassen sind direkter zu verstehen. Jede Indirektionsebene kostet mentale Energie.

Wartbarkeit:

Premature Abstraction erzeugt Boilerplate ohne Nutzen. Refactoring zu einem Interface ist später einfach.

Erweiterbarkeit:

Das Interface entfaltet seinen Wert, wenn Erweiterungen tatsächlich kommen – nicht vorher.

Hinterlasse einen Kommentar

I’m Iman

Mein Name ist Iman Dabbaghi. Ich arbeite als Senior Software Engineer in der Schweiz. Außerdem interessiere ich mich sehr für gewaltfreie Kommunikation, Bachata-Tanz und Musik sowie fürs die Persönlichkeitsentwicklung.

Ich habe einen Masterabschluss in Informatik von der Universität Freiburg in Deutschland, bin Spring/Java Certified Professional (OCP), Certified Professional for Software Architecture (CPSA-F) und ein lebenslanger Lernender 🎓.

EN:

My name is Iman Dabbaghi. I work as a Senior Software Engineer in Switzerland. I am also very interessted in nonviolent communication, Bachata dance and music and also for personal development.

I hold a masters degree in computer science from the university of Freiburg in Germany, am a Spring / Java Certified Professional (OCP), Certified Software Architecture (CPSA-F) and Life Long Learner🎓

Let’s connect