“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); }