One of my favorite features with C# 3.0 is the extension methods. An excellent way to apply some cross cutting concerns and great tool for attaching utility functions. Heavily used by the LINQ frameworks and in most utility classes I’ve seen around .NET 3.5 projects. Some common example usages I’ve come across include:
1: var name = "OlympicGames2010".Wordify();
2: var attributeValue = typeof(Customer).Attribute(o => o.Status)
3: .Value();
Lately I’ve started to expand the the modeling ideas I tried to explain during my presentation at Öredev 2008. It became more of a discussion with James O. Coplien then a presentation and I was far from done with my own understanding of the ideas and issues I’d identified (there are things improve in this content). The core idea is pretty simple though:
Not all consumers of an object are interested of the same aspects of that object, it will take on different roles in different contexts
Let me explain with the simplest example; when building an order system, the aspects of a product that the order think is important are usually not exactly the same aspects that the inventory system value.
Contexts in object models
Eric Evans touches this in his description of “bounded contexts” (Domain Driven Design p335) where he stresses the importance of defining contexts where a model is valid and not mix it into another context. In essence the model of a product should be duplicated, once in the order context and once in the inventory context.
This is a great principle but at times it be too coarse-grained. James Coplien and Trygve Reenskaug have identified this in their work around, what they call, “DCI architecture”. Richard Öberg et al have done some work in what they call qi4j where they are composing objects with bits and pieces instead of creating full blown models for each context.
Slicing logic and models using Extension Methods
Let’s get back to the extension methods and see how they can help us slice business logic up in bits and pieces and “compose” what we need for different contexts.
In the code base I’m writing for my TechDays presentation I have a warehouse class that holds stock of items. These Items are common for different contexts, they will surface in orders and PLM. One of the features in this application is to find a stock to reserve a given an item. The following code is used to find that stock:
1: return Stock.First(stock => stock.Item == item);
Although trivial, this is a business rule for the warehouse. When the warehouse class evolved this piece of rule would be duplicated in methods like Reserve, Releaes and Delete. A classic refactoring would be to use Extract Method to move it out and reuse that piece, something like:
1: private bool Match(Stock stock, ItemInfo item)
2: {
3: return stock.Item == item
4: }
5: ...
6: return Stock.First(stock => Match(stock, item));
This is a completely valid refactoring but honestly we loose some intent, the immediate connection with stock and item are not as explicit and the lambda expression wasn’t simplified that much.
So let’s Refator to Slice instead:
1: public static class ItemInAWarehouseSlices
2: {
3: public static bool Match(this ItemInfo @this, Stock stock)
4: {
5: return stock.Item == @this;
6: }
7: }
Adding this business rules as an extension method gives us a natural place for the code to live and a mechanism to use to compose objects differently in different contexts. Importing this extension method class into the Warehouse C#-file, ItemInfo will provide the logic needed in that context;
1: return Stock.First(item.Match);
Adding the rule this way also sprinkles a touch of DSL on it and gives it a semantic meaning which makes the code make more sense.
Why don’t you just put that method on the ItemInfo, you migh ask. Well the answer is simple. ItemInfo is a concept that might be shared across contexts. Contexts that have no grasp of what a Stock is, nor should it. If I’d add everything I needed to the ItemInfo class for all contexts that uses Item. I would be in a bad shape. Thus the ideas behind Contextual Domain models, Bounded Context, DCI and Composite objects.
Extend away …
So extension methods have other usages then just extending existing classes with some utility. It’s also a powerful tool to slice your logic in composable pieces which helps you focus on the aspects you think is important in the current context you are working.
So what do you think? Will this help you create clear separtions?
#1 by Marco staffoli on November 19, 2010 - 13:08
Great!
I have 3 applications that use the same domain model. So i was thinkin about a solution to share the common business logic and add “specific” business logic in the different context.
I come in the same your idea: extension method.