#TIPP: #Fluent #Interfaces
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.
Kategorie: .NET, Handwerk, Tipp 6 Kommentare »

am 29. Juli 2009 um 16:16 Uhr | #
Sehr schön erklärt
am 29. Juli 2009 um 16:56 Uhr | #
Kann mich Thomas nur anschließen; Top erklärt.
am 29. Juli 2009 um 18:56 Uhr | #
Kleine Anmerkung.
Die Verwendung von “Set” bei den Methoden-Namen ist meines Erachtens unnötig und stört auch den Lesefluss.
config.Text(“Hallo, Welt”).Height(20).Width(40).Execute();
Desweiteren wäre ein kleiner Hinweis auf ein
return this;
der bei jeder Fluent-Methoden Implementierung notwendig ist hilfreich.
am 30. Juli 2009 um 14:45 Uhr | #
@Albert: Ist eingebaut. Das “Set” lasse ich mal drin, das ist ja eher Geschmackssache.
am 19. August 2009 um 00:13 Uhr | #
Hi,
Hi,
ich habe mich im Zuge meiner Bachelorarbeit mit dem Thema beschäftigt. Das Prinzip nach dem du hier Fluent Interfaces beschrieben hast kann man noch so erweitern, dass diese nicht nur als Konfigurator-Objekt verwendet werden können. Das eigentlich schöne an einem Fluent Interface ist ja, dass es sich so gut wie selbst erklärt. Man kann sofort ohne Dokumentation erkennen wofür die einzelnen Parameter da sind. Aber was wenn man vielleicht möchte, dass die Methoden nur in einer bestimmten Reihenfolge aufgerufen werden dürfen oder ganz bestimmte Methoden immer aufgerufen werden müssen? Das würde dem Benutzer eines Fluent Interfaces davor bewahren, dass Fluent Interface falsch zu benutzen. Aber ein entsprechendes Fluent Interface zu erstellen ist wesentlich aufwändiger. Und genau hier setzt meine Bachelorarbeit an. Ich habe versucht das Erstellen von Fluent Interfaces so weit wie möglich zu erleichtern. Mit Hilfe eines Modellers ist es nun möglich Fluent Interfaces in Diagrammen zu modellieren aus denen dann direkt der Programm-Code generiert werden kann.
Schaut mal einfach unter: http://www.fluent-interfaces.com
Zwar wird zur Zeit nur Java-Code generiert, aber das ganze ist so konzipiert, dass man schnell auf eine andere Sprache umsteigen kann.
Grüße
Philipp
am 18. September 2009 um 07:27 Uhr | #
Hallo an alle,
ich habe mich im Zuge meiner Bachelorarbeit mit dem Thema der Fluent Interfaces etwas intensiver beschäftigt. Die Art wie du das Fluent Interface verwendet hast findet man ganz häufig und ist sicher auch am schnellsten implementiert. Aber man kann mit Fluent Interfaces noch viel mehr machen. Man kann Schnittstellen bauen die sich beinah von selbst erklären und kaum falsch verwendet werden können. Was ist zum Beispiel, wenn du möchtest, dass die Methoden in einer bestimmten Reihenfolge aufgerufen werden oder dass bestimmte Methoden immer aufgerufen werden?
Du könntest so z.B. nie einen Satz erzwingen der in etwa so aussieht:
polyogn().with(punkt(1,1)).and(punkt(2,2);
Damit der Benutzer des Fluent Interface nach “polygon()” nur “with()” aufrufen kann, müssen wir hier einen Typen zurück liefern, der auch nur die Methode bereitstellt. Das gleiche gilt auch für “with” und “and”. Man erstellt so einer Art Geflecht von Typen. Bei größeren Fluent Interfaces ist das natürlich sehr aufwändig. Darum habe ich einen Tool gebaut mit dem man Fluent Interfaces in Form eines Diagrammes erstellen kann aus dem dann der nötige Code generiert wird. Zwar kann derzeit nur Java-Code generiert werden aber es ist kein Problem das ganze auch für C# oder ähnliches anzupassen. Das geht sogar relativ schnell.
Unter <a href="http://www.fluent-interfaces.com"http://www.fluent-interfaces.com findet ihr mehr.