AJ's blog

March 16, 2008

Using extension methods for decomposition

Filed under: .NET, .NET Framework, C#, LINQ, Software Developers, Software Development — ajdotnet @ 12:18 pm

My last post dug into some technical details of extension methods. Now I’ll have a look at what extension methods can do for me; more to the point, whether extension methods may support new patterns or idioms. Occasional hints in other blogs and articles as well as my own findings revealed two general patterns:

  • Using extension methods for decomposition or separation
  • Using extension methods to support fluent interfaces

I’ll start with the first one because I think it may address a variety of issues and ideas. Fluent interfaces will be covered in another post.

Let’s go through some use cases.

Interface based coding

Did you ever sort a collection? What method did you use? Array.Sort()? List.Sort()? Why not IList.Sort()? Because there is no IList.Sort()!

One may argue that Microsoft simply forgot to put Sort() into the IList interface or to provide an ISortedList interface. But what about Project(), RemoveInvalidValues(), Normalize()? Matter of fact, it is simply not possible to foresee every demand and not feasibly to put it into general purpose interfaces.

The price to pay is that implementing sorting in the list classes sacrifices interface based abstraction and binds the calling code unnecessarily to implementation classes. But what other options did we have anyway?

The sorting algorithm could easily be implemented working on IList, independent of the implementation class. Yet the method would have to be put somewhere, usually in some helper class (like ListHelper.Sort(IList)). Microsoft did not do that because it violates ease of use principles outlined in “Framework Design Guidelines” (indeed a very valid reason).

However, since .NET 3.5 we can easily solve that problem by declaring our static Sort method as extension method, making it appear as if it is a method of the interface:

void ListHelper.Sort(this IList) {…}

OK, one step back! Doesn’t that violate OO principles? Shouldn’t the the list be able to sort itself? Just like a deck of cards? Well, last time I played cards I had to sort … I mean shuffle them. Sorting and shuffling are algorithms working on lists, not intrinsic features of said lists.

Thus we have a possible idiom:

  1. Provide interfaces that describe core characteristics of data structures (such as IEnumerable, IList, ISerializable, …).
  2. Put methods that implement algorithms working on core characteristics in extension classes for the respective interface.

This helps keeping interfaces straight, and it improves reuse of algorithms.

Segregate context specific service methods

Take an entity class, say Address. There are certain things one could do with an address, e.g. provide a formatted string for printing, checking if it is a valid address, and so on. I said “do with” not “do to”; again I’m talking about algorithms working on the address, rather than manipulating it. And they don’t need access to the inner workings of the object.

Still we could implement the methods within the address class; the motivation for the previous pattern was reuse of the algorithm, but how many addresses are we going to have?

So let’s put the Validate() method (used by the business logic) into the entity, the FormatForPrinting() Method (used by the UI), the GenerateUniqueId() Method (used by the data access layer), … damn, that format method produced HTML but this client needs XAML… . And is this IsReadOnly() method for the UI or the data access layer?
Got the idea? There is reason not to put those methods into the entity class: they do not only depend on the entity but also belong to their specific context, in this case a certain layer.

Putting those methods into a helper class could have been done before, but again, how many helper classes shall we have to remember? Extension methods can solve that particular problem.

So, I have another idiom:

  1. Keep context dependent methods out of classes which are used in different contexts.
  2. Put those context specific methods into context specific extension classes.

Doing this, the entity class stays clean and pure, no undue features, no pesky helper methods, only nice little properties.

Separation of core implementation and service methods

Take one of my favorite classes, a factory class. It probably provides a CreateObject(…) method taking certain parameters that does the main job of creating objects. Of course it would take the maximum of available parameters, and of course it would have certain overloads to make things easier to use:

object CreateObject(Type type, params object[] args);
// Service methods:
T CreateObject<T>(params object[] args);
object CreateObject(Type type);
T CreateObject<T>();
object CreateObject(string typeName, params object[] args);
object CreateObject(string typeName);

I wouldn’t have problems to further add type safe overloads. What’s the result? While there is just one relevant method, a bunch of additional method complicates the class, broadens its surface, needs to be tested, may do things a little differently… and at the end the overload I just needed would still be missing.

Extension methods solve this problem in two aspects: First they allow moving the convenience methods out of the factory class without sacrificing the usability from the callers point of view. Secondly, I can add my special incarnation of a CreateObject method in my private extension class and it would seamlessly appear beside (or even replace) all the other methods.

So, I have another idiom:

  1. Keep the implementation of classes to the core functionality.
  2. Put additional convenience methods into extension classes.

Mixin classes

The idea of using extension methods for mixins was introduced with Create Mixins with Interfaces and Extension Methods. This article uses extension methods working on a special interface, the combination of them forming the mixin. A class that needs some orthogonal functionality would simply implement that interface and benefit from the provided implementation.

The idiom described looks like:

If you have an algorithm or feature you want to apply in an egalitarian way to different classes…

  1. use an interface to provide uniform access to the classes…
  2. and provide the algorithm as extension method to that interface.

I may have seen those two points before…?!

As a side note: Wikipedia defines mixins as follows:

“[…] a mixin is a class that provides a certain functionality to be inherited by a subclass, but is not meant to stand alone. Inheriting from a mixin is not a form of specialization but is rather a means to collect functionality.” http://en.wikipedia.org/wiki/Mixin

Seen from a C++ point of view, the mixin approach relies on several tenets: Based on inheritance of functionality, the mixin class is a fully fledged class that provides actual code (as opposed to interfaces). This quite often included code placed in constructors and the destructor, thus implicitly adding behavior. At the same time, the reuse by inheritance approach does not interfere with the regular inheritance hierarchy, as C++ supports multiple inheritance. And it also does not interfere with the interface of the class (i.e. it can be realized that way in case it is undesired), as C++ allows protected and private inheritance.

Of course this is not possible with .NET, as multiple inheritance is restricted to interfaces and always public. Thus I am inclined to call the mentioned approach a very poor realization of mixins. It falls short of covering any aspect I used mixins for with C++: I don’t inherit functionality, say by providing default implementations. I don’t add behavior, as no logic is changed until I explicitly call the extension method. And I cannot even dream of making this an implementation detail of the class, as it solely relies on changing the public interface of that class.

Putting things together…

These usage scenarios may have provided different motivations, but in essence they have followed the same ideas and led to similar idioms. In a more generalized form:

  1. Keep methods out of your class or interface …
    • if they implement algorithms working on the class rather than features of the class
    • if they depend not only on the class/interface, but also on the context
    • if they merely add convenience overloads
    • if they would not have been available in the first place
  2. Provide extension classes …
    • that capture algorithms working on objects and especially implementations working on interfaces
    • that cluster context specific features
    • that add shortcuts, convenience methods, and helper methods.

Now, all this could have been done before with static helper classes. And having extension methods at our disposal doesn’t change the work on the providers side at all. The big difference however is at the users side. While the classes he works with remain simpler and more concise at first glance, just by adding a using statement he can dynamically enhance those classes with exactly the bunch of features he just needs. This is almost like dynamically extending class definitions in dynamic languages.

Putting it together, this has (or rather may have) several effects:

  • It supports the information hiding principle for abstract data types as only the absolutely necessary methods get access to the inner workings.
  • It elevates separation of concerns as classes and algorithms are separated.
  • It improves cohesion as context dependent features are moved to their respective context.
  • Reusability should improve if the extension methods can be implemented to work against interfaces and thus become more applicable.
  • Testing those algorithms should improve as those interfaces may also be implemented by mock objects.
  • Testing the classes will be easier due to the smaller and more concise surface to test.
  • And finally classes and interfaces that have a smaller surface are less complex and easier to keep consistent.

If you ask me, I didn’t expect such a compelling list of benefits when I started locking into this topic. Whether they may be realized remains to be seen, but after having worked on this post, I am willing to give it a try.

That is, as long as nobody points out any flaws I overlooked. (OK guys, that’s a challenge!)

That’s all for now folks,
AJ.NET

kick it on DotNetKicks.com

Advertisement

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: