Archiv für 29. Juli 2009


#TIPP: #Fluent #Interfaces

29. Juli 2009 - 11:02 Uhr

Nicht ganz neu, aber ungemein praktisch ist die Idee von Fluent Interfaces. Der Gedanke dahinter ist sehr simpel: Alle Methoden haben eine Rückgabe vom Typ eines definierten Interfaces oder der jeweiligen Klasse. Der Effekt: Die Lesbarkeit wird gesteigert und der Schreibaufwand verringert sich.

Im folgenden Beispiel seien ein Interface IConfiguration und ein Interface IConfigurationFluent definiert, die die gleiche Funktionalität abbilden:

/// <summary>
/// Setzt Konfigurationsinformationen
/// </summary>
public interface IConfiguration
{
   /// <summary>
   /// Breite setzen
   /// </summary>
   void SetWidth(int width);

   /// <summary>
   /// Höhe setzen
   /// </summary>
   void SetHeight(int height);

   /// <summary>
   /// Text setzen
   /// </summary>
   void SetText(string text);

   /// <summary>
   /// Ausführen
   /// </summary>
   void Execute();
}

/// <summary>
/// Setzt Konfigurationsinformationen auf angenehmere Art und Weise
/// </summary>
public interface IConfigurationFluent
{
   /// <summary>
   /// Breite setzen
   /// </summary>
   IConfigurationFluent SetWidth(int width);

   /// <summary>
   /// Höhe setzen
   /// </summary>
   IConfigurationFluent SetHeight(int height);

   /// <summary>
   /// Text setzen
   /// </summary>
   IConfigurationFluent SetText(string text);

   /// <summary>
   /// Ausführen
   /// </summary>
   IConfigurationFluent Execute();
}

Der zusätzliche Definitionsaufwand ist minimal, die Handhabung des Codes nimmt jedoch deutlich zu. Sehen wir uns an, wie man mit dem herkömmlichen Interface arbeitet:

/// <summary>
/// Konfiguriert ein Element auf die "klassische" Art
/// </summary>
public void Configure(IConfiguration config)
{
   // Höhe setzen
   config.SetHeight(20);

   // Breite setzen
   config.SetWidth(40);

   // Text setzen
   config.SetText("Hallo, Welt");

   // Ausführen
   config.Execute();
}

Nichts dagegen einzuwenden. Störend ist nur, das Sie ständig "config" schreiben müssen und Operationen, die zusammen gehören, einzeln durchführen.

Hier ist der funktional gleichwertige Code unter Verwendung des Fluent-Interfaces:

/// <summary>
/// Konfiguriert ein Element auf die "fluente" Art
/// </summary>
public void Configure(IConfigurationFluent config)
{
   // Höhe, Breite, Text setzen und Operation durchführen
   config.SetHeight(20)
         .SetWidth(40)
         .SetText("Hallo, Welt")
         .Execute();
}

Hinweis: Da die implementierten Methoden alle die jeweilige Instanz wieder zurückgeben sollen, muss an deren Ende stets return this; eingefügt werden. Für die Methode SetHeight() könnte eine entsprechende Implementierung also so aussehen:

/// <summary>
/// Setzt die Höhe
/// </summary>
public IConfigurationFluent SetHeight(int height)
{
   // Wert setzen
   this.height = value;

   // Aktuelle Instanz zurück geben
   return this;
}

Wie gesagt: Funktional ist dieser Code gleichwertig, in der alltäglichen Anwendung aber um einiges angenehmer, als der klassische Code. Aus genau diesem Grund wird dieser Ansatz von immer mehr Frameworks eingesetzt – bekanntester Fall ist das NHibernate-Framework, das sehr extensiv davon Gebrauch macht.

6 Kommentare » | .NET, Handwerk, Tipp