Archive for the ‘Codestyle’ Category

Effective Java: Builder-Pattern

Wednesday, January 15th, 2020

7 Jahre nach der ersten Auflage ist das Buch “Effective Java” von Joshua Bloch nun endlich in der zweiten Auflage erschienen. Der Klassiker wurde komplett runderneuert: Best-Practises für ‘neue’ Sprachelemente wie Generics und Enums kamen hinzu, veraltete Kapitel (wie der Ersatz von C-Strukturen) wurden gestrichen.

Neu ist auch das “Builder-Pattern”. Es kommt zum Einsatz, wenn ein Objekt viele optionale Variablen besitzt. Die bisherige, klassische Variante bei solchen Objekten ist das Schreiben von mehreren Konstruktoren (’telescoping constructor’), die über unterschiedliche Anzahl von Parametern verfügen. Allerdings sinkt die Lesbarkeit bei mehr als 6 Parametern gewaltig (vor allem wenn die Parameter vom gleichen Typ sind).

Das “Builder-Pattern” verzichtet auf die ‘telescoping constructors’ und arbeitet stattdessen mit einer statischen inneren Klasse, die für das Erzeugen einer Instanz der äusseren Klasse zuständig ist. Der Konstruktor des Builder-Objekts enthält alle “Pflicht”-Variablen, die optionalen Variablen werden über (verkettbare) Methodenaufrufe hinzugefügt. Abschliessend wird die build Methode aufgerufen, die ein unveränderliches Objekt der äusseren Klasse erzeugt. Der Konstruktor der äusseren Klasse ist privat, d.h. nur ein Builder kann ein Objekt tatsächlich instanzieren:

Hier ein Beispiel:

  1. package ch.javablog.builderpattern;
  2.  
  3. import java.math.BigDecimal;
  4.  
  5. public class Product {
  6.         // all fields are final: immutable object
  7.         private final String id;
  8.         private final BigDecimal price;
  9.         private final String salesDescription;
  10.         private final Product baseProduct;
  11.         private final boolean approved;
  12.  
  13.         private Product (Builder builder) {
  14.                 // private Constructor can only be called from Builder
  15.                 this.id = builder.id;
  16.                 this.price = builder.price;
  17.                 this.salesDescription = builder.salesDescription;
  18.                 this.baseProduct = builder.baseProduct;
  19.                 this.approved = builder.approved;
  20.         }
  21.  
  22.         public String getId() {
  23.                 return id;
  24.         }
  25.  
  26.         public BigDecimal getPrice() {
  27.                 return price;
  28.         }
  29.  
  30.         public String getSalesDescription() {
  31.                 return salesDescription;
  32.         }
  33.  
  34.         public Product getBaseProduct() {
  35.                 return baseProduct;
  36.         }
  37.  
  38.         public boolean isApproved() {
  39.                 return approved;
  40.         }
  41.  
  42.         public static class Builder {
  43.                 // mandatory parameter
  44.                 private final String id;
  45.                 private final BigDecimal price;
  46.  
  47.                 // optional
  48.                 private String salesDescription = “”;
  49.                 private Product baseProduct     = null;
  50.                 private boolean approved        = true;
  51.  
  52.                 public Builder(String id, BigDecimal price) {
  53.                         this.id = id;
  54.                         this.price = price;
  55.                 }
  56.                 public Builder salesDescription(String salesDescription) {
  57.                         this.salesDescription = salesDescription;
  58.                         return this;
  59.                 }
  60.                 public Builder baseProduct(Product baseProduct) {
  61.                         this.baseProduct = baseProduct;
  62.                         return this;
  63.                 }
  64.                 public Builder approved(boolean approved) {
  65.                         this.approved = approved;
  66.                         return this;
  67.                 }
  68.  
  69.                 public Product build() {
  70.                         return new Product(this);
  71.                 }
  72.         }
  73. }
  74.  
  75. public class ProductClient {
  76.         public static void main(Stringargs) {
  77.                 BigDecimal price = BigDecimal.valueOf(36.5);
  78.                 Product.Builder builder = new Product.Builder(“050-0047”,price);
  79.                 // create Product using the builder
  80.                 Product p = builder
  81.                                 .salesDescription(“great product”)
  82.                                 .approved(false)
  83.                                 .build();
  84.         }
  85. }

OVal – the object validation framework for Java 5 or later

Tuesday, December 17th, 2019

Ich muss gestehen, ich bin ein Fan von defensiver Programmierung, d.h. ich deklariere mögliche Inkonsistenzen von Objekten durch das Werfen von RuntimeExceptions (z.B. IllegalStateException oder IllegalArgumentException). Praktisch finde ich ebenfalls das Prinzip der Unveränderlichkeit. Einmal erzeugt, soll das Objekt nicht mehr verändert werden. Somit werden Inkonsistenzen (z.B. nicht gesetzte Attribute) bereits im Vorfeld vermieden.

Allerdings verursacht diese Form der Absicherung unschönen Code-Overhead, da jeder Parameter manuell überprüft werden muss. Zudem stehen unveränderliche Objekte im Widerspruch mit der JavaBean Spezifikation

Vor kurzem habe ich einen interessanten Lösungsansatz im OVal Framework entdeckt. Hier werden die Properties und Methoden mit Annotations versehen und explizit oder implizit (via AspectJ) validiert. Beispiele gibts heute keine, stattdessen verweise ich auf die (gute) Doku.

Buchtipp: Effektiv Java programmieren

Monday, July 30th, 2018

“Effektiv Java programmieren” (ISBN 3-8273-1933-1) von Joshua Bloch gehört eindeutig zur Pflichtlektüre jedes professionellen Javaprogrammieres. In 57 kurz gehaltenen Kapiteln werden Regeln und Tipps für einen robusten und sauberen Programmierstil definiert. Als kleinen Appetitanreger fasse ich zwei seiner Regeln zusammen.

Thema 9: Überschreiben Sie toString immer.

Die toString Methode von java.lang.Object gibt standardmässig den Klassennamen und den Hashcode des Objects zurück. Für Debuggingzwecke ist diese Information nicht besonders aussagekräftig, von daher sollte jede Klasse diese Methode überschreiben und in ihr sämtliche relevanten Inhalte des Objekts zurückgeben.

Beispiel:toString Methode aus SquareTile (Klasse zur Speicherung einer quadratischen Bildkachel in einer Kartenanwendung). Hier gibt die toString Methode die Koordinate der linken oberen Kante und die Breite zurück. Das ist beim Debugging (bzw. beim Logfile-Lesen) wesentlich hilfreicher als der Hashcode.

  1. public String toString() {
  2. return  “topLeftCoord: “ + topLeftCoord.toString() + \t +
  3. “width == height: “ + width;
  4. }

Thema 13: Bevorzugen Sie Unveränderbarkeit

Unveränderliche Klassen erlauben lediglich bei der Konstruktion des Objekts das Setzen von Werten. Im Nachhinein können keine Werte mehr gesetzt werden (Es gibt also keine Setter-Methoden). Zudem sind alle Felder als final und private deklariert.

Vorteil: Die Anzahl möglicher Zustände reduziert sich dramatisch, da das Objekt nach dem Konstruktor fertig initialisiert ist. Es gibt also keine unfertigen Objektstati, die man bei Methodenaufrufen überprüfen muss. Zudem sind solche Klassen automatisch Thread-sicher.
Die Javabibliothek verwendet viele unveränderliche Klassen, z.B. java.lang.String oder java.math.BigInteger .

Nachteil: Für jeden Wert muss ein eigenes Objekt erzeugt werden. Bei vielen und grossen Objekten kann dies recht kostspielig werden (bekanntester Effekt: Stringverkettung). Benötigt eine unveränderliche Klasse zudem viele Initialisierungsparameter, so leidet darunter die Lesbarkeit des Konstruktors, da hier alle Parameter übergeben werden müssen.