Oder: ab wann hilft der ContextResolver?

CDI – teile und reduziere

Sven Ruppert
©Shutterstock.com/maximmmmum

Im unserer Serie haben wir uns in dem vorherigen Artikel mit dem Konstrukt ContextResolver [1] auseinandergesetzt. Ab wann lohnt sich der Einsatz? Wie sieht es mit einem praktischen Beispiel aus? Anhand eines SimpleDateFormatter werden wir dieses nun darstellen.

Oft findet man Stellen im Quelltext ähnlich Listing 1 in der ein SimpleDateFormat definiert wird. Hier sind mehrere Dinge die aus der Perspektive der entkoppelten Systeme auffallen sollten.

  private final SimpleDateFormat sdf 
   = new SimpleDateFormat("yyyy.MM.dd",Locale.GERMAN);


SimpleDateFormat zu trivial?

Bei der Definition des SimpleDateFormat werden zwei Dinge dem Konstruktor mit übergeben. Der erste Parameter definiert das Ausgabeformat. In unserem Beispiel wird folgendes Pattern verwendet: „yyyy.MM.dd“. Es bedeutet, dass ein Datum z.B. der 21.3.2013, als String in der Form „2013.03.21“ ausgegeben werden soll. Der zweite Parameter gibt an, für welches Ziel-Locale die Ausgabe erzeugt werden soll, für den Fall, dass länderspezifische Elemente im ersten Parameter vorhanden sind.

Beide Angaben sind Kandidaten für die Auslagerung aus dem statischen Kontext, da angenommen werden kann, dass es sich im Laufe der Anwendungszeit ändern kann/wird.

Beginnen wir mit dem Locale. Wenn kein Locale angegeben wird, geht die Implementierung davon aus, dass die aktuelle default-Einstellung in der JVM verwendet wird. In vielen Fällen mag das auf der Client-Seite passend sein, auf der Server-Seite meist überhaupt nicht.

Schritt I: Das Locale

Gehen wir also davon aus, dass wir eine Implementierung (LocaleResolver) haben, die in der Lage ist, dass benötigte Locale zu ermitteln. Dann verändert sich Listing 1 zu Listing 2.

final LocaleResolver localeResolver = new LocaleResolver(); 

private final SimpleDateFormat sdf 
   = new SimpleDateFormat("yyyy.MM.dd", localeResolver.getLocale());


Nun ist die Ermittlung des benötigten Locale verlagert und wenn die Implementierung der Klasse LocaleResolver es hergibt, auch dynamisch zur Laufzeit bestimmt.

Schritt II: Das Pattern – yyyy.MM.dd

Der kritische Teil ist allerdings die Definition des Pattern (Parameter 1). Hier lässt sich ähnlich wie in Listing 2 ein Resolver implementieren. Nennen wir diesen PropertyResolver. Auch hier sollte die Auflösung eines Patterns zur Laufzeit dynamisch erfolgen. An dieser Stelle möchte ich anmerken, dass die meisten Pattern leider nicht fachlich definiert werden. Nennen wir also „yyyy.MM.dd“ dem Kontext (z.B. Rechnungseingang) entsprechend um in „rechnungsdatum“. Daraus ergibt sich nun Listing 3.

final LocaleResolver localeResolver = new LocaleResolver();
final PropertyResolver propertyResolver = new PropertyResolver(); 

private final SimpleDateFormat sdf 
= new SimpleDateFormat(propertyResolver.resolve("rechnungsdatum"),
      localeResolver.resolveLocale());


Schritt III: Refactoring / Kompaktifizierung

Nun sind sicherlich die einzelnen Komponenten mehr getrennt als zu Beginn, allerdings ist der hier gezeigte Quellcode nicht kompakt. Aus Sicht des Entwicklers und als Abstraktion auf rein fachliche Inhalte ist Listing 4 erstrebenswerter.

private @Inject 
@CDISimpleDateFormatter(„rechnungsdatum“) 
SimpleDateFormat sdf; 


Das kann durch die Verwendung eines Producers erreicht werden. Wichtig ist an dieser Stelle die Verwendung eines nonbinding Attributes innerhalb der Annotation @CDISimpleDateFormatter (Listing 5) zur Übergabe der fachlichen Bezeichner.

@Qualifier
@Retention(value = RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface CDISimpleDateFormatter {
   @Nonbinding String value() default "";
}


Die Implementierung des Producers selbst ist sehr einfach gehalten und erhält hier nur eine minimale FallBack-Lösung.

public class SimpleDateFormatterProducer {

    public SimpleDateFormat createDefault(InjectionPoint 
injectionPoint) {
        final String ressource =
propertyResolver.resolveProperty("date.default");
        final Locale locale = localeResolver.resolveLocale();
        return new SimpleDateFormat(ressource, locale);
    }


    private @Inject LocaleResolver localeResolver;
    private @Inject PropertyResolver propertyResolver;

    @Produces
    @CDISimpleDateFormatter
    public java.text.SimpleDateFormat
       produceSimpleDateFormatter(InjectionPoint ip) {
            final Annotated annotated = ip.getAnnotated();
            final Class <CDISimpleDateFormatter > type = 
               CDISimpleDateFormatter.class;
            final boolean present = 
               annotated.isAnnotationPresent(type);
            if (present) {
            final CDISimpleDateFormatter annotation = 
               annotated.getAnnotation(type);
            final String ressourceKey = annotation.value();
            final String ressource =
               propertyResolver.resolveProperty(ressourceKey);
            if (ressource.equals("###" + ressourceKey + "###")) {
                return createDefault(ip);
            } else {
                final Locale locale = localeResolver.resolveLocale();
                return new SimpleDateFormat(ressource, locale);
            }
        } else {
            return createDefault(ip);
        }
    }
}


Fazit

Schon bei diesem einfachen Beispiel konnte gezeigt werden, dass mit ein wenig mehr Aufwand an der richtigen Stelle eine sehr hohe Flexibilität und Entkopplung erreicht werden kann. Dreh- und Angelpunkt ist hier der Konstrukt „ContextResolver“ in Form eines LocaleResolvers und PropertyResolvers. Selbst wenn zu Beginn lediglich triviale Implementierungen verwendet werden, ergibt sich eine sofortige, positive Wirkung auf die Codequalität. Wir konnten damit bei größeren Migrationsprojekten (von Swing zu CDI/JavaFX) auch starke Reduktionen bei der Anzahl Quelltextzeilen erreichen.

Die Quelltexte zu diesem Text sind unter [2] zu finden. Wer umfangreichere Beispiele zu diesem Thema sehen möchte, dem empfehle einen Blick auf [3]. (Modul cdi-commons)  

Aufmacherbild: flat icons for web and mobile applications with beverages (flat design with long shadows) von Shutterstock / Urheberrecht: maximmmum

Geschrieben von
Sven Ruppert
Sven Ruppert
Sven Ruppert arbeitet seit 1996 mit Java und ist Developer Advocate bei Vaadin. In seiner Freizeit spricht er auf internationalen und nationalen Konferenzen, schreibt für IT-Magazine und für Tech-Portale. Twitter: @SvenRuppert
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.