CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Dave Laribee

"Whoso would be a man must be a nonconformist." - Ralph Waldo Emerson

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.


Published Jun 16 2008, 01:06 PM by Dave Laribee
Filed under: , , , ,

Comments

Dave Laribee said:

Thanks for catching that. Fixed!

# June 16, 2008 2:01 PM

Michael O. Schoneman said:

I think:

if (Started != null) Starting(this, EventArgs.empty);  

should be:

if (Started != null) Started(this, EventArgs.empty);  

# June 16, 2008 2:37 PM

Jason Meridth said:

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.

# June 16, 2008 3:08 PM

Dave Laribee said:

@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...

# June 16, 2008 4:12 PM

Skwr said:

It should be Empty not empty :)

# June 16, 2008 8:31 PM

Reflective Perspective - Chris Alcock » The Morning Brew #116 said:

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #116

# June 17, 2008 2:48 AM

Dew Droplet - June 17, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Droplet - June 17, 2008 | Alvin Ashcraft's Morning Dew

# June 17, 2008 11:43 AM

Haacked said:

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. ;)

# June 17, 2008 12:08 PM

Dave Laribee said:

@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...

# June 17, 2008 8:35 PM

Colin Jack said:

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.

# June 18, 2008 2:15 PM

Dave Laribee said:

@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.

# June 18, 2008 4:16 PM

Nate Kohari said:

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.

# June 19, 2008 9:04 AM

Dave Laribee said:

@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.

# June 19, 2008 12:27 PM

Colin Jack said:

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.

# June 19, 2008 2:02 PM

Dave Laribee said:

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

# June 19, 2008 2:40 PM

Colin Jack said:

@Dave

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

# June 20, 2008 3:05 AM

make money online said:

Wonderful. I wish everyone posted such great content. Thanks. Sam.

# June 30, 2008 7:43 AM

Dave Laribee said:

The next leg of our quest to uncover the deeper driving forces behind SOLID principles brings us to the

# September 11, 2008 1:04 AM

Community Blogs said:

The next leg of our quest to uncover the deeper driving forces behind SOLID principles brings us to the

# September 11, 2008 1:33 AM

Mirrored Blogs said:

The next leg of our quest to uncover the deeper driving forces behind SOLID principles brings us to the

# September 11, 2008 2:09 AM

Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas

# September 11, 2008 5:12 AM

Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas

# September 13, 2008 8:55 PM

Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas

# September 15, 2008 9:05 AM

Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas

# September 16, 2008 9:09 AM

Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas said:

Pingback from  Why SOLID? GIMME AN O! - taccato! trend tracker, cool hunting, new business ideas

# September 21, 2008 9:28 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors