Sharing common functionality across controllers is something that I have run into several times in the past. It is basically needing to offer the same functionality across different elements in the application.
Let us take for a moment a search page. In my current application, a search page has to offer rich search functionality for the user, the ability to do pattern matching, so given a certain entity, match all the relevant related entities that can fit this entity. Match all openings for a candidate. Match all candidates for an opening.
That is unique, mostly, but then we have a lot of boiler plate functionality, which moves from printing, paging, security, saving the query and saving the results, changing the results, exporting to XML, loading saved queries and saved results, etc, etc etc. Those requirements are shared among several
On the right you can see one solution for this problem, the Template Controller pattern. Basically, we concentrated all the common functionality into the Base Specification Controller.
What you can't see is that the declaration of the controller also have the following generic constraints:
public class BaseSpecificationController<TSpecification, TEntity> : BaseController where TSpecification : BaseSpecification<TEntity>, new() where TEntity : IIdentifable
This means that the base controller can perform most of its actions on the base classes, without needing to specialize just because of the different types.
Yes, dynamic language would makes things much easier, I know.
Note that while I am talking about sharing the controller logic here, between several controllers, we can also do the same for the views using shared views. Or not. That is useful if we want to use different UI for the search.
In fact, given that we need to show a search screen, it is not surprising that we would need a different UI, and some different behavior for each search controller, to get the data required to specify a search.
Now that we have the background all set up, let us see what we can do with the concrete search controllers, shall we.
You can see the structure of them on the right. The search candidates is doing much more than the search orders, but a lot of the functionality between the two is shared. And more importantly, easily shared.
Well, if you define the generics syntax above as easy, at least.
The main advantage of this approach is that I can literally develop a feature on the candidates controller, and then generalize it to support all the other searches in the application.
In this scenario, we started with searching for candidates, and after getting the basic structure done, I moved to start working on the search orders.
At the same time, another guy was implementing all the extra functionality (excel export, sending SMS and emails, etc).
After we I finished the search order page, we merged and refactored up most of the functionality that we needed in both places.
This is a good approach if you can utilize inheritance in your favor. But there is a kink, if you want to aggregate functionality from several sources, then you are going to go back to delegation or duplication.
Adam has interesting discussion about this issue, and an interesting proposition. But that will be another post.
The question then becomes how do you easily test this. I have always had a hard time testing things that had heavy inheritance. Or is that just me? :)
Abstract Test Fixture is the answer to that, in my case.