Helper classes are the dumping ground for more or less closely related methods that do not warrant a top level type by themselves. They tend to be not very well organized, semantically disjoint and hard to use. One particularly insidious type of helper class is the third party control helper. It hides all the knowledge required to link the controls with the application data behind a series of methods created piecemeal and consequently with poorly defined argument lists. Just today I was coding away yet another one of these helpers, this time to configure Telerik’s chart for WPF. After some time the code that called the helper was:
And the helper was composed of a series of methods with names such as AddSeries , AddMapping, etc, etc. It was obvious that this was not going to end well, especially because I want to use this chart in many different places in the application.
The solution I came up with was the creation of a very small DSL (Domain Specific Language) to take care of the chart configuration. The DSL is composed through method chaining JQuery’s way and the main advantages of this approach are that it can enforce specific sequences of actions and can be extended in a predictable way. The resulting calling code needs to know more about Telerik’s structures and that is the price to pay for the expandability of the interface. On the other hand the intent is more clearly expressed, for example:
RadChartBuilder.Configure(chart) .AddSeriesDefinition(new PieSeriesDefinition()) .AddMapping("Values", DataPointMember.YValue) .AddMapping("ColumnNames", DataPointMember.LegendLabel) .CloseMappings() .BindTo(myDataObject);
Where “chart” is a RadChart control. I am re-using Telerik’s PieSeriesDefinition and DataPointMember which require some insider knowledge of the control but hopefully it is obvious that the first one defines the type of chart whereas the second one determines which property of the data source is bound to which part of a chart.
You may have noticed the CloseMappings() method which is not required for the chart but that is used to transition between “branches” of the DSL. This is an interesting technique I applied by varying the returned type depending on the internal state of the configuration. There are two states in the process which I am calling branches: the main branch which is composed by AddSeriesDefinition and BindTo methods and the mappings branch which is composed by the IAddMapping interface (AddMapping and CloseMappings). The point here is that I want to enforce the addition of mappings after the addition of the series definition. So AddSeriesDefinition returns IAddMapping instead of RadChartBuilder forcing the developer to add mappings. CloseMappings returns to the main branch. It is hard to explain the mechanics of the process but the code is very simple and should be readily understandable.
This approach can be used for any type of configuration process and, I suspect, to most helper classes out there. Projects such as NHibernate and NInject use this approach which has been gaining support as a replacement for XML configuration files. I think this is a way to turn tedious code into a productive exercise in language definition.
You can download the code from here.
The usual caveats and disclaimers apply. The code is specific to Telerik’s WPF RadChart which is a commercial product.