Hook Methods

I’ve written about the template method pattern before. For my money it’s still a very useful pattern for building super lightweight frameworks and enabling the open-closed principle which states:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

So let’s review quickly what a template method looks like and what it can do for our code:


public abstract class PriceCalculator
{
  public Money Calculate(Product product)
  {
    Money retailPrice = GetRetailPrice();
    Discount discount = CalculateDiscount(product);
    return discount.Apply(retailPrice);
  }
  
  public abstract Discount CalculateDiscount(Product product);
  
  public Rate GetRetailPrice() { //... }
}

public class GoldCustomerPriceCalculator : PriceCalculator
{
  public override Discount CalculateDiscount(Product product)
  {
    // Gold customers get 20% off, OR
    // We could use per-product discounts.
    return new Discount(20); 
  }
} 

public class StandardCustomerPriceCalculator : PriceCalculator
{
  public override Discount CalculateDiscount(Product product)
  {
    // Standard customers receive no discount
    return new Discount(0); 
  }
}

It’s pretty easy to see we’ve gotten some variation. This is a simple example off the top of my head and there’s probably a better way to tackle this domain problem, but it shows template method in action so I’ll leave it alone.

This is all well and good, but what if we encountered a scenario where our class could function autonomously but we want to provide an hook or extensibility point for edge cases or later extension (beware of YAGNI on the latter). I’ll call this technique a hook method.

What’s a hook method? By playing with the physics of template method a bit to we can make derivations elective by lifting the abstract constraint on our base class and making the template method virtual. The former move is optional and the later is required. Your solution will dictate whether or not the base class should or should not be declared abstract.

Let’s consider an example where we want a start/finish semantic for an implementation of model view presenter. In this example I want to have a base presenter that provides some common functionality: attaching the presenter to the view and raising events to signify the presenter has been started. I want to provide a hook so my presenter implementations can do some processing, call services, obtain reference data, etc. I can accomplish this with a hook method style of template method like so:


public abstract class Presenter : IPresenter
{

  public void Start()
  {
    if (Starting != null) Starting(this, EventArgs.Empty);
    View.Attach(this);
    Startup();
    if (Started != null) Started(this, EventArgs.Empty);
  }
  
  protected virtual void Startup() {};
  
  public event EventHandler Starting;
  public event EventHandler Started;
}

Or we can return to the contrived example of the price calculator to illustrate a non-abstract class:


public class PriceCalculator
{
  public Money Calculate(Product product)
  {
    Money retailPrice = GetRetailPrice();
    Discount discount = CalculateDiscount(product);
    return discount.Apply(retailPrice);
  }
  
  public virtual Discount CalculateDiscount(Product product)
  {
    return new Discount(0);
  }
  
  public Rate GetRetailPrice() { //... }
}

public class GoldCustomerPriceCalculator : PriceCalculator
{
  public override Discount CalculateDiscount(Product product)
  {
    // Gold customers get 20% off, OR
    // We could use per-product discounts.
    return new Discount(20); 
  }
}

Money price = new PriceCalculator.Calculate(someProduct);
// or
Money price = new GoldCutomerPriceCalculator.Calculate(someProduct);

I’m sure many of you have seen examples of this in the wild. I’d file this under “pattern life hack.” That is, a simple and subtle shift in thinking around template method that expands its many uses in DRYing up your codebase.

This entry was posted in .net, C#, code, design, patterns. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

15 Responses to Hook Methods

  1. Colin Jack says:

    @Dave
    Excellent, looking forward to the visitor/anti-corruption entries.

  2. Dave Laribee says:

    Good point. Another: template method *can* really limit testing liability (as testing goes into the supertype).

  3. Colin Jack says:

    On the composition argument, even if you did use composition you might well use a template method approach if all the strategies share some aspects of their behavior.

  4. Dave Laribee says:

    @Nate – Sure. I’ve got a bad example here. Where inheritance is a natural choice is layer supertypes. The presenter example is a better application and these kinds of things tend to be the exception to the “favor composition over inheritance” rule.

  5. Nate Kohari says:

    This is a good pattern to follow, but I would argue that you should typically favor composition over inheritance. In a lot of cases, this problem can be solved via dependency injection (really just the strategy pattern) — by introducing a DiscountCalculator dependency, for example, which can be injected into the PriceCalculator.

    Sometimes, when using the template method pattern, you might just be giving the *appearance* of extensibility. For example, if PriceCalculator had a bunch of different template methods rather than just the one for calculating discounts, you would be forced to implement each of these in a subtype just to alter the discount calculation. If you instead create a dependency that fulfills each of those concerns, you might end up making your type easier to extend.

  6. Dave Laribee says:

    @Colin

    Blogging again?! Okay, okay, so I slowed way down there for a while 😉

    I can tackle that problem in an upcoming post, sure. I’m not done with specifications yet, so let me clear that. I had in mind this technique about anti-corruption layers and the visitor pattern for pulling stuff in your model which I’ll get to (probably) next week.

  7. Colin Jack says:

    The virtual method approach, with a do-nothing implementation in the base class, we’ve used that a lot. Very useful approach

    @David
    Since you’re blogging again are you thinking of doing posts on your domain model, for example the way you structure your different sub domains and use anti-corruption layers between them. You’ve only alluded to this stuff in the past and it seems quite interesting to me.

  8. Dave Laribee says:

    @Phil – Maybe. It depends :) I can see their point; why not let the “main” method of the pattern be overridden? Obviously you know this, but virtual means proxy-able and testable…

  9. Haacked says:

    Nice description. I find this funny because we (mvc team) get chastised for following this pattern. Someone would come along and say, “Hey, public Money Calculate(Product product) should be virtual!!!” which defeats the whole point of the Template method pattern. 😉

  10. Skwr says:

    It should be Empty not empty :)

  11. Dave Laribee says:

    @Michael – Wow, I’m batting a thousand today. Thanks for the heads up. It’s corrected now.

    @Jason – RE: your preference. I hear you loud and clear. Where template method can really shine (and inheritance in general) is in layer supertypes. For types with more diverse variation I go with the conventional “favor composition” wisdom. I bought the book you mentioned but haven’t read it yet. Will have to dig in a bit more…

  12. The Design Patterns in Ruby book also has an excellent example and explanation of the Hook Methods.

    I prefer Strategy to avoid inheritence, but you can’t avoid it sometimes.

    Good post David.

  13. Michael O. Schoneman says:

    I think:
    if (Started != null) Starting(this, EventArgs.empty);
    should be:
    if (Started != null) Started(this, EventArgs.empty);

  14. Dave Laribee says:

    Thanks for catching that. Fixed!

  15. guy says:

    Your standardcustomer class has the same discount as your gold one. HTH

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>