HowTo: Chaining in .Net mit Fluent Interfaces

ChainingChaining (dt. Verkettung) ist ein Entwicklungskonstrukt bei dem Methodenaufrufe direkt aneinander “gekettet” werden. Chaining ist seit der Veröffentlichung von jQuery  im Entwicklermainstream angekommen. Was für Webentwickler daily business ist, ist allerdings noch nicht in bei allen anderen Plattformen Gang und Gebe.

Mit einfachen Mitteln kann dennoch jeder .Net Entwickler seine Objekte chainable machen. Der von Eric Evans und Martin Fowler vorgeschlagene Weg zur Implementierung objekteorientierter Schnittstellen kurz “ fluent interfaces ”  bietet den hierzu benötigten Ansatz ohne dass irgendwelche Frameworks oder Fremdsourcen in die eigene Solution integriert werden müssen.

 

Durch fluent interfaces können Objekte verkettet werden und die Schnittstellen sind lesbarer!

 

Anhand eines kleinen Beispiels sollte die Realisierung von Chaining in .Net klarer werden.

   1:  public interface IUiElement
   2:  {
   3:     void SetHeight(int height);
   4:     void SetWidth(int width);
   5:     void SetTitle(string title);
   6:  }

Verwendet wird eine Implementierung des IUiElement Interfaces demnach wie folgt

   1:  public void DoSomeUiActions(){
   2:    IUiElement uiElement = new TextBox();
   3:    uiElement.SetHeight(20);
   4:    uiElement.SetWidth(100);
   5:    uiElement.SetTitle("Eine einfache Textbox");
   6:  }

 

Mit dem fluent interfaces Ansatz würde das IUiElement allerdings so aussehen

   1:  public interface IUiElement
   2:  {
   3:    IUiElement SetHeight(int height);
   4:    IUiElement SetWidth(int width);
   5:    IUiElement SetTitle(string title);
   6:  }

 

Was auf den ersten Blick etwas gewöhnungsbedürftig für den alt eingesessenen .Net Entwickler wirkt, wird aber bei der Verwendung einer konkreten IUiElement Instanz klarer.

   1:  public void DoSomeUiAction()
   2:  {
   3:   IUiElement uiElement = new TextBox();
   4:   uiElement.SetHeight(20)
   5:            .SetWidth(100)
   6:            .SetTitle("chained textbox");
   7:  }

Wie man direkt sieht wird der Code dadurch gestrafft und wirkt auch direkt schon logisch zusammenhängend.

Dieses Konstrukt kann sehr gut dabei helfen flexible Repositories zu erstellen, da Filter zum auslesen von Instanzen aus einem Repository einfach gechained werden können. Hier ein Beispiel dazu

   1:  public class UiElementService : IUiElementService
   2:  {
   3:    //...
   4:    IUiElementRepository repository;
   5:    // ...
   6:   
   7:    public UiElementService(IUiElementRepository repository)
   8:    {
   9:      this.repository = repository;
  10:    }
  11:    public List<IUiElement> GetElements()
  12:    {
  13:      return repository.GetUiElements().ToList<IUiElement>();
  14:    }
  15:    public List<IUiElement> GetElements(int height, string title)
  16:    {
  17:      return repository.GetUiElements()
  18:             .HeightEquals(height)
  19:             .TitleContains(title)
  20:             .ToList<IUiElement>();
  21:    }
  22:  }
  23:   
  24:  public class MockUpUiRepository : IUiElementRepository
  25:  {
  26:   
  27:    public IQueryable<IUiElement> GetUiElements()
  28:    {
  29:      TextBox tb = new TextBox();
  30:   
  31:      List<IUiElement> mockupElements = new List<IUiElement>()
  32:    {
  33:        
  34:      new TextBox() { 
  35:          Height = 20, 
  36:          Width = 110, 
  37:          Title ="TextBox" },
  38:      new RadioButton() {
  39:          Height = 20, 
  40:          Width = 200, 
  41:          Title =".Net is cool?!"},
  42:      new CheckBox() {
  43:          Height = 19, 
  44:          Width = 300, 
  45:          Title="Test the checkbox"}
  46:     };
  47:      return mockupElements.AsQueryable();
  48:    }
  49:   
  50:   
  51:  }
  52:   
  53:  public static class UiRepositoryFilters
  54:  {
  55:    public static IQueryable<IUiElement> HeightEquals(
  56:        this IQueryable<IUiElement> query,
  57:        int height)
  58:    {
  59:      return (from e in query
  60:              where e.Height == height
  61:              select e);
  62:    }
  63:   
  64:    public static IQueryable<IUiElement> TitleContains(
  65:        this IQueryable<IUiElement> query,
  66:        string title)
  67:    {
  68:      return (from e in query
  69:              where e.Title.Contains(title)
  70:              select e);
  71:    }
  72:   
  73:    public static IQueryable<IUiElement> TitleStartsWith(
  74:        this IQueryable<IUiElement> query,
  75:        string title)
  76:    {
  77:      return (from e in query
  78:              where e.Title.StartsWith(title)
  79:              select e);
  80:    }
  81:  }

Wie man sieht ist das Chaining Konzept vielseitig und leicht einsetzbar, dadurch kann die Service-Fasade sehr flexible gestaltet werden. Die eigentliche Servicefasade kann dann sehr einfach in der Anwendung verwendet werden:

   1:  IUiElementService Service = 
   2:    new UiElementService(new MockUpUiRepository());
   3:  Service.GetElements(20, "Net").ForEach(
   4:    delegate(IUiElement element)
   5:  {
   6:      Console.WriteLine(
   7:        String.Format("Element ({0}) | Weite ({1})",
   8:        element.Title,element.Height));
   9:  });

Mir persönlich gefällt der chaining Ansatz sehr gut, weil wie bereits gesagt, logisch zusammenhängende Aufrufe noch enger zusammenstehen im Code. Außerdem wird der Code dadurch um einiges straffer.

 

Was haltet Ihr von Chaining in .Net ?

 

Technorati-Tags: ,,,
Published Freitag, 14. August 2009 22:16 von ThorstenHans
Abgelegt unter: , , ,

Kommentare

# Chaining in .Net mit Fluent Interfaces

Freitag, 14. August 2009 23:06 von dotnet-kicks.de

Sie wurden gekickt (eine gute Sache) - Trackback von dotnet-kicks.de

# Contextual Binding mit Ninject

Montag, 25. Januar 2010 22:00 von .NET rocks

Im einem meiner letzten Beitrag habe ich Ninject vorgestellt und eine grobe Einweisung gegeben, wie man

# Interne DSL in C#

Mittwoch, 7. April 2010 22:35 von .NET rocks

Vor einiger Zeit habe ich bereits einen Post über Chaining in .NET mit Fluent Interfaces geschrieben

Kommentar abgeben

(verpflichtend) 
(verpflichtend) 
(optional)
(verpflichtend)