#TIPP: #Fluent #Interfaces
29. Juli 2009 - 11:02 UhrNicht 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.
