Angelika Langer Trainerin in Java und C++

Bei dem Execute-Around-Pattern geht es darum, Copy-and-Paste-Programmierung, also redundanten Code, zu vermeiden.

Klaus Kreft Consultant und Trainer

Beim Execute-Around-Pattern wird nicht mehr die eigentliche Methode implementiert, sondern nur noch ein Lambda-Ausdruck, der an die Hilfsmethode mit dem redundanten Code übergeben wird.

Dieses Mal wollen wir uns ein Beispiel für eine Programmiertechnik ansehen, die ohne Lambdas wenig attraktiv wäre: Es geht um das so genannte Execute-Around-Pattern. Das ist eine Programmiertechnik, mit der sich redundanter Code elegant vermeiden lässt. Am Beispiel von Execute Around wollen wir außerdem auf einige Aspekte der neuen Sprachmittel eingehen, die gelegentlich übersehen oder ignoriert werden, weil sie lästig sind.

Wir haben das Execute-Around-Pattern schon einmal kurz als Beispiel für eine Programmiertechnik erwähnt, die sich mit den neuen Sprachmitteln der Lambdas elegant umsetzen lässt. Bei dem Execute-Around-Pattern, geht es darum, Copy-and-Paste-Programmierung, also redundanten Code, zu vermeiden. Dabei wird strukturell ähnlicher Code so zerlegt, dass die immer wiederkehrende, identische Struktur herausgelöst und in eine Hilfsmethode ausgelagert wird. Der Teil, der variiert, wird an die Hilfsmethode als Lambda-Ausdruck übergeben.

Betrachten wir ein Beispiel: Nehmen wir an, wir haben eine Klasse TimeInterval, die eine Zeitspanne repräsentiert, zum Beispiel die Zeitspanne von 13:00 Uhr bis 13:45 Uhr. Die Klasse TimeInterval könnte aussehen wie in Listing 1.

public final class TimeInterval {
  private LocalTime lower, upper;
  public TimeInterval(LocalTime l, LocalTime u) {
    lower = l;
    upper = u;
  }
  public void setLower(LocalTime l) {
    if (l.isBefore(upper)||l.equals(upper)) {
      lower = l;
    }
  }
  public LocalTime getLower() {
    return lower;
  }
  public void setUpper(LocalTime u) {
    if (lower.isBefore(u) || lower.equals(u))  {
      upper = u;
    } 
  }
  public LocalTime getUpper() {
    return upper;
  }
  public boolean contains(LocalTime i) {
    return (lower.isBefore(i)||lower.equals(i)) && (i.isBefore(upper)||i.equals(upper));
  }
}

Dabei ist LocalTime eine Abstraktion aus dem java.time-Package des JDK, das es seit Java 8 gibt. Nehmen wir mal an, wir wollen in allen relevanten Methoden der Klasse die Prüfung hinzufügen, ob die Zeitspanne gültig ist. Das wäre eine Überprüfung der so genannten Klasseninvariante (engl. Class Invariant). Die Klasseninvariante ist ein Element des Design-by-Contract-Programming. Die Idee des Design-by-Contract besteht darin, dass für einen Typ und seine Operationen Vorbedingungen (engl. Preconditions), Nachbedingungen (engl. Postconditions) und die schon erwähnte Invariante definiert werden. Sie bilden den Vertrag (engl. Contract), den die Implementierung des Typs erfüllen muss. Die Precondition beschreibt, welchen Bedingungen und/oder Zustände am Anfang einer Operation herrschen müssen, damit die Operation überhaupt ausgeführt werden kann. Die Postcondition beschreibt, welche Bedingungen und/oder Zustände die Operation hinterlässt, wenn sie fertig abgelaufen ist. Die Pre- und Postconditions beziehen sich auf die einzelnen Operationen und sind für jede Operation anders. Die Invariante ist von den einzelnen Operationen unabhängig und beschreibt Bedingungen und Zustände, die für jedes Objekt zu Beginn und am Ende jeder Operation erfüllt sein müssen. Insbesondere in der Objektorientierung wird erwartet, dass jede Operation, die auf ein Objekt angewandt wird, das Objekt von einem gültigen Zustand in den nächsten gültigen Zustand überführt. Diese Idee eines gültigen Zustands ist genau das, was die Invariante ausdrückt.

X
- Gib Deinen Standort ein -
- or -