Skip to content

Lambdas in Java

Gerade bastle ich an einem Wrapper für DataSources der im Umgang mit Datenbanken helfen soll (zusätzliche Statistiken, blocking bei DB Fehlern und pausieren von DB Anfragen). Dabei wrappe ich zwei Methoden getConnection() und getConnection(String, String). Nur der Aufruf der realen Methode unterscheidet sich, der restliche Code ist in beiden Methoden gleich:

CODE:
public Connection getConnection(String user, String pass) ... {     verifyState();       while(true) {         try {             Connection c = ds.getConnection(user, pass);             verifyConnection(c); // throws SQLEx             return c;         } catch (SQLException e) {             handleException(e); // throws SQLEx         }     } } public Connection getConnection() ... {     verifyState();       while(true) {         try {             Connection c = ds.getConnection();             verifyConnection(c); // throws SQLEx             return c;         } catch (SQLException e) {             handleException(e); // throws SQLEx         }     } }

Im Sinne von DRY (don't repeat yourself) ist dies aber unschön, weil der (in Realität noch komplexere) Retry code doppelt vorkommt, und ich immer beide Methoden anpassen muss. Dieses Problem kommt oft bei Frameworks vor, und nennt sich "the whole in the middle" Muster. Eine Lösung wäre:

CODE:
public Connection getConnection(String user, String pass) {     return smartConnect(true, user, pass); } public Connection getConnection() {     return smartConnect(false, null, null); } Connection smartConnect(boolean hasArgs, String user, String pass) {     verifyState();       while(true) {         try {             Connection c;             // --- the whole in the middle             if (hasArgs)                 c = ds.getConnection(user, pass);             else                 c = ds.getConnection();             // ---             verifyConnection(c); // throws SQLEx             return c;         } catch (SQLException e) {             handleException(e);         }     } }

Das funktioniert aber nur im einfachsten Fall und es verkompliziert leider den Framework code, was auch wieder der Verständlichkeit schadet.

In C# 3.0 kann man das (wie ich grade gesehen habe) mit einer Lambda Action lösen (man kann also im Prinzip anonyme Funktionsblöcke übergeben). In Java müßte man dazu ein Objekt übergeben:

CODE:
public Connection getConnection(String user, String pass) {     return smartConnect(         new ConnectionProvider() {              Connection provide() { return ds.getConnection(user, pass); }}); } public Connection getConnection() {     return smartConnect(         new ConnectionProvider() {             Connection provide() { return ds.getConnection(); }}); } Connection smartConnect(ConectionProvider cp) {     verifyState();       while(true) {         try {             Connection c = cp.provide();             verifyConnection(c); // throws SQLEx             return c;         } catch (SQLException e) {             handleException(e); // throws SQLEx         }     } } abstract class ConnectionProvider {   Connection provide(); }

Der smartConnect() code wird damit lesbarer, und man kann auch komplexere Aktivitäten injizieren (eventuell mit Argumenten zur provide() methode), aber man muss jetzt noch eine extra Klasse definieren und alles in allem wird es auch mehr Code. Bei jedem Aufruf wird ein zusätzliches Objekt erzeugt. Ich denke ich werde also eher bei dem "if" Ansatz bleiben.

Ich vermute mal der Syntaktische Zucker "Lambda Expression" erzeugt bei C# auch ein extra Call Objekt, aber es fällt wesentlich weniger zusätzlichen Code beim Aufruf an.