Delegating Decorators

asp.net mvc, asp.net, code 0 comments suggest edit

When approaching an extensibility model, I often find cases in which I want to merely tweak the existing behavior of the default implementation and wish I didn’t have to create a whole new specific type to do so.

paint

Instead of creating a specific type, I tend to write a decorator class that implements the interface and takes in both the default instance of that interface and a delegate (specified using a lambda of course).

Let’s look at a quick example to make all this abstract talk more concrete. I’m playing around with the NVelocity View Engine for ASP.NET MVC from the MvcContrib project. Rather than have each controller specify the view engine, I’d really like to specify this in just one place.

I could take the time to setup a DI container, but I’m feeling lazy and this is just a simple prototype application so I planned to just implement the IControllerFactory interface and have it set the view engine after creating the controller.

For those not intimately acquainted with ASP.NET MVC, you can override how the framework instantiates controllers by implementing the IControllerFactory interface.

However, at this point, I realized I was creating these one-off controller factories all the time. What I really wanted was some way to decorate the existing controller factory with a bit of extra logic. What I did was write an extension method that allowed me to do the following in my Global.asax.cs file.

ControllerBuilder.Current.Decorate(
  (current, context, controllerName) => 
  {
    var controller = current.CreateController(context, controllerName) 
      as Controller;
    if (controller != null) {
      controller.ViewEngine = new NVelocityViewFactory();
    }
    return controller;
  }
);

It’s a little funky looking, but what is happening here is that I am calling a new Decorate method and passing it a lambda. That lambda will be used to decorate or wrap the current controller factory and will get called when it is time to instantiate a Controller instance.

However, the lambda always receives the original controller factory so it can use it if needed. So in effect, the lambda wraps or decorates whatever the current controller factory happens to be.

In this case, all my lambda is doing is using the default controller to create the controller, and then it sets a property of that controller after the fact. However, if I wanted to, I could have had my lambda completely override creating the controller.

Here is the the code for the extension method to ControllerBuilder.

public static class ControllerFactoryExtensions {
  public static void Decorate(this ControllerBuilder builder
  ,Func<IControllerFactory, RequestContext, string, IController> decorator) {
    IControllerFactory current = builder.GetControllerFactory();
    builder.SetControllerFactory(
      new DelegatingControllerFactory(current, decorator));
  }
}

As you can see, under the hood, I am actually replacing the current controller factory with a new one called DelegatingControllerFactory. But the implementation for this new factory is really simple. It simply calls a delegate that you supply. As far as the user of the Decorate method is concerned, this class doesn’t really exist.

internal class DelegatingControllerFactory : IControllerFactory {
  Func<IControllerFactory, RequestContext, string, IController> _factory;
  IControllerFactory _wrappedFactory;

  public DelegatingControllerFactory(IControllerFactory current
    , Func<IControllerFactory
    , RequestContext, string, IController> factoryDelegate) {
    if (factoryDelegate == null) {
      throw new ArgumentNullException("factoryDelegate");
    }
    _wrappedFactory = current;
    _factory = factoryDelegate;
  }

  IController IControllerFactory.CreateController(RequestContext context
  , string controllerName) {
      return _factory(_wrappedFactory, context, controllerName);
  }

  void IControllerFactory.DisposeController(IController controller) {
    if (controller is IDisposable) {
      ((IDisposable)controller).Dispose();
    }
  }
}

With this in place, the next time I need to implement a one-off controller factory, I can simply decorate the current controller factory instead.

I’m starting to find myself using this pattern in a lot of places. The potential downside of this approach is that if someone else comes along who has to maintain it, they might find it difficult to understand if they’re not well acquainted with lambdas and delegates.

So it is a bit of a tradeoff between convenience for the code author and readability for the code reader.

Technorati Tags: aspnetmvc,controller factor,decorator,patterns

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

7 responses

  1. Avatar for Josh Stodola
    Josh Stodola June 18th, 2008

    Yeah... code like that leads to broken monitors!

  2. Avatar for Steven Burman
    Steven Burman June 18th, 2008

    I think implementing a container would have been less effort... and more readable ;)
    But I guess this does separate you from an external dependency which is generally a good thing, where possible.

  3. Avatar for haacked
    haacked June 18th, 2008

    @Steven yeah, probably true the first time. But the next time when I'm just getting setup, this is just one simple method call. I can always swap it out later with a container approach, but this is nice scaffolding while I prototype my app.

  4. Avatar for Gauthier Segay
    Gauthier Segay June 19th, 2008

    That's a nice trick, however I'm concerned by the fact it will wrap the call multiple times (deep call stack), won't the event approach became safer allowing such extension?

    In this case, all my lambda is doing is using the default controller to create the controller


    Did you mean "my lambda is using the default controller factory"?

  5. Avatar for Damon Carr
    Damon Carr June 19th, 2008


    I see this a lot lately in C# 3.0 implementations and it perhaps I am 'old-school' but if I saw this in the wild more (nothing I say here is unique) I might not feel compelled.
    As 'service providing' code (especially decorator style adornments) is migrated away from any pure contractual representation of our expectations of the partitipants in our complex system interactions (this assumes the code spans projects and has a central architectural focus).
    In my opinion I treat this pattern you have described (as well as the more common 'single method/property interface' marker which is then extended at will using extension methods as 'design debt'.
    All things in moderation I suppose.. What do you think? I get concerned now as I am seeing so much of a swing away from 'contract driven' services and using interfaces as concrete immutable items for our hinges. Sure it's a pain but I don't think managing free-form namespace imports for extension methods is workable in large scale software.
    I prefer to preach a fairly conservative line which I know you also are well aware of:
    1) A 'service' which is aggregated by it's consumer is typically preferred to inheritance (how many times have you heard that one - grin)...
    2) To define an unambiguous definition for interoperability and versioning, a service that is non-trivial has some 'pure abstract' contract, and due to .NET that means an Interface more then not.
    3) As mentioned, leveraging interesting optimizations when resolving service dependencies for injection rather then matching code to run-time state via typical prop/constructor injection.
    Again, nothing you don't know and in the Agile 'design debt' sense as long as this style is managed (I do it all the time) it's quite productive.
    I've also seen an alarming number of others dramatically violate 'single responsibility' design in passing Linq expressions that are lazilly resolved between tiers. For example, in the MVP pattern code that constructs the elements of a to be compiled expression tree partially in the view, then in the presenter, and even adding a dash of data in the domain model to be invoked.. UGH.. It's not as bad a literal dependencies I suppose but it's still white box..
    Is there a decent contractual obligation we can expose using this pattern that would satisfy what I am getting at?
    Kind Regards and well done,
    Damon Wilder Carr
    http://damon.agilefactor.com/

  6. Avatar for Lance Fisher
    Lance Fisher June 23rd, 2008

    I like this a lot. I like lambdas more and more every day. This the first time I've seen an example of a lambda with more than one line of code. If I had seen that when I was first introduced to lambdas, I would have learned them quicker. With one-line lambdas, there is a lot implied, and that makes the learning curve a little steeper.
    The thing I like about using anonymous functions is that you don't have to name them! I got this attitude from working with JavaScript. Naming is hard, but often unnecessary.

  7. Avatar for Blackjack
    Blackjack February 6th, 2011

    this is great thanks. I agree with Lance, lambdas are what makes this great !
    Or dont I get it ?