Finger weg von Apache Commons

“if all you have is a hammer, everything looks like a nail”

Maslow’s hammer

Wenn es eine beliebte Sammlung von Bibliotheken für Java Entwickler gibt, dann ist dies wohl Apache Commons. Für jede Art von Problem eines Java Entwickler gibt es dort eine Methode, die eine einfache Lösung verspricht.

Viele Methoden kümmern sich dabei um eine angenehme Umgehung einer potentiellen NullPointerException.

class TitleServiceImpl  implements TitleService {
  void validTitle(Ancestor ancestor, String... acceptedTitles) {
    final String title = ancestor.getTitle();
    if (StringUtils.containsAny(value, acceptedTitles)) {
      doTitleThings(ancestor);
    }
  }
}

In diesem Beispiel wird mitder StringUtils.containsAny Methode geprüft, ob der Titel des Ahnen in der Liste der offiziellen Titel vorhanden ist. Auf dem ersten Blick erscheint die Tatsache sehr angenehm, dass title und acceptedTitles keinerlei Inhalt benötigen. Wenn es einen Titel gibt und wenn es offizielle Titel gibt, dann kann ein Match gefunden werden.

An diesem Beispiel ist aber leider alles furchtbar. Da ist zum einen der Anwendungsfall dieser Methode.

titleService.validTitle(myAncestor, "Kaiser", "König", "Edelmann", "Bürger", "Bauer", "Bettelmann");

Die Hoffnung es Entwickler ist, dass die Methode doTitleThings aufgerufen wird, wenn der Ahne einen der hier angegebenen Titel besitzt. Aber die Methode wird auch aufgerufen, wenn der Titel Königsberger Klopse lautet oder Bauernopfer. Denn es wird geprüft, ob einer der angegebenen Titel innerhalb des Ahnentitels zu finden ist.

Andererseits ist es die Methode aus der Commons Bibliothek an sich, die nicht wirklich elegant ist.

public static boolean containsAny(final CharSequence cs, final CharSequence... searchCharSequences) {
  if (isEmpty(cs) || ArrayUtils.isEmpty(searchCharSequences)) {
    return false;
  }
  for (final CharSequence searchCharSequence : searchCharSequences) {
    if (contains(cs, searchCharSequence)) {
      return true;
    }
  }
  return false;
}

Die Methode isEmpty prüft den title auf null und in der contains Methode wird wiederum bei jedem Schleifendurchlauf geprüft, ob title den Wert null besitzt. So sieht es in vielen der Commons Methoden aus, etliche Schichten von Prüfungen um jeden Fall möglichst generisch zu behandeln.

Grundsätzlich ist dies kein großes Übel, denn die Rechner sind schnell und die Code Optimierung der Java Umgebungen exzellent. Problematisch ist aber die Mitkopplung durch die Commons Bibliothek. Die Mitkopplung oder auch Positive Rückkopplung ist vielen Entwicklern durch das Werfen von Exception bekannt. Das Phänomen ist im Beitrag Fehlerbehandlung aus der Hölle beschrieben.

Wird von einer Methode Exception geworfen, dann muss sie von der aufrufenden Methode gefangen oder weitergeworfen werden. Wird sie einfach weitergeworfen, dann breitet sich das Werfen von Exception im eigenen Code wie eine Lawine aus. Lawinen entstehen ihrerseits auch durch positive Rückkopplung von rutschenden Schnee auf ruhenden Schnee.

Im Fall der Commons Methoden wird der Entwickler gezwungen, immer wieder Bibliotheksfunktionen zu verwenden oder tatsächlich aktuelle Java Methoden und das Null Pattern zu nutzen. Eine erweiterte validTitle muss weiterhin title auf null prüfen, da dies alle verwendeten Bibliotheksmethoden auch prüfen.

class TitleServiceImpl  implements TitleService{
  void validTitle(Ancestor ancestor, String... acceptedTitles) {
    final String title = ancestor.getTitle();
    if (StringUtils.equals(title, "Königsberger Klopse")) {
      throw new IllegalArgumentException();
    }
    if (StringUtils.containsAny(value, acceptedTitles)) {
      doTitleThings(ancestor);
    }
  }
}

Hier nun die Prüfung auf Königsberger Klopse im Apache Commons Stil. An dieser Stelle sollte dem Leser eine Träne über die Wage rollen, denn String Konstanten sind Objekte auf denen Methoden aufgerufen werden können. Leider vergessen viele Commons Anhänger mit der Zeit banalste Konstrukte und Konzepte der Programmiersprache Java.

class TitleServiceImpl  implements TitleService {
  void validTitle(Ancestor ancestor, String... acceptedTitles) {
    String title = ancestor.getTitle();
    if ("Königsberger Klopse".equals(title)) {
      throw new IllegalArgumentException();
    }
    if(acceptedTitles != null && title != null && Arrays.asList(acceptedTitle).contains(title)) {
      doTitleThings(ancestor);
    }
  }
}

Noch angenehmer wird der Code, wenn tatsächlich das Null und das Guard Decorator Pattern berücksichtigt werden und keine null Werte an die Methode übergeben werden.

class TitleServiceImpl implements TitleService {
  void validTitle(Ancestor ancestor, String... acceptedTitles) {
    if(Arrays.asList(acceptedTitle).contains(ancestor.getTitle())) {
      doTitleThings(ancestor);
    }
}

@AllArgsConstructor
class CrapTitleServiceImpl implements TitleService {
  private final TitleService wrapped;

  void validTitle(Ancestor ancestor, String... acceptedTitles) {
    if ("Königsberger Klopse".equals(ancestor.getTitle())) {
      throw new IllegalArgumentException();
    }
    wrapped.validTitle(ancestor, acceptedTitles);
}

Schreibe einen Kommentar