Learn the Template Method Design Pattern in .NET C# and explore the different possibilities.
What is Template Method Design Pattern?
The Template Method Design Pattern is one of the widely used design patterns, especially when dealing with algorithmic modules or systems. Let me walk you through it step by step.
Sometimes when we are working on a module of our system, we find that we need to follow some specific steps in a certain order. But, we also find that the implementations of these steps are not always the same. In this case, what would we do?
Here the Template Method Design Pattern comes to rescue. It is one of the behavioral design patterns. It allows the developer to define a procedural sequence of an algorithm or a process in a base class which delegates the specific implementations of some steps to the subclasses without changing the overall structure.
Do you get it? No?… Let me show you.
Code Example
Let’s say that our system is a Tax Calculator Console Application. The accountants consulting our team provided us with a general formula for calculating taxes but they told us that some steps of the general formula would differ in calculation from one person to another depending on their marital status.
In other words, all the steps in the general formula should be there in the main sequence but in some cases the way we are calculating some of these steps would be different.
Therefore, our team tried to come up with the best design and decided to use the Template Method Design Pattern as it was recommended by some expert solution architects.
So, following the Template Method Design Pattern our solution would be as in the code below.
TaxCalculator
What we can notice here:
This is an abstract class to represent the base class for all our concrete Tax Calculators.
On the CalculateTaxes method, we are implementing the general formula we got from our accountants.
We defined the three methods CalculateExpenses, CalculateIncomeTax, and CalculateVat as protected abstract because these methods would have different implementations and these implementations would be provided by our concrete Tax Calculators.
TaxCalculator1 and TaxCalculator2
What we can notice here:
These are the two concrete Tax Calculators implementations we have.
They inherit from our base TaxCalculator class.
They provide the implementations of the three methods CalculateExpenses, CalculateIncomeTax, and CalculateVat.
Sometimes one or more of these methods could require a dummy implementation (like returning zero) to act as if they were never called.
Console Application
What we can notice here:
This is the Console Application.
In the Run method we are assuming that we would need to use both TaxCalculator1 and TaxCalculator2 but in the real world it could be only one of them.
Now, when the CalculateTaxes method is called on an instance of TaxCalculator1, the implementations of the three methods provided by TaxCalculator1 would be executed.
On the other hand, when the CalculateTaxes method is called on an instance of TaxCalculator2, the implementations of the three methods provided by TaxCalculator2 would be executed.
Now, running this application would return the expected calculation result as our accountants advised. However, is this the best solution?
Design Challenges
The current design is working, but, it is not the best design we can come up with.
Here are some problems with the current design:
Useless/Dummy method calls are sometimes performed just because we are keeping the general base formula intact. This is not good for performance.
It violates the Interface Segregation Principle of the SOL(I)D principles. It is easy to spot this for the same reason as point 1 above.
Therefore, on the next sections, we would dig deeper into each of these design problems and try to come up with a better design.
Useless/Dummy Method Calls
Here is something that some developers don’t recognize. When a method call is performed, some memory allocations happen for input parameters and return object unless they are passed and returned by reference.
Therefore, when we just call a method even if the call is not needed, this is not the best design.
I can now hear someone saying:
You are just exaggerating. It is not that of a big deal.
Ok, may be you are right if it is as in the current implementation of our Tax Calculator Application.
However, what if we are dealing with more complex objects and structs that consume more memory?
Or, what if our CalculateTaxes method is called too many times that even the smallest deviation in the memory allocation of one call turns up eventually to be huge?
Let’s say that our code is updated as follows:
Console Application
See, here our CalculateTaxes method is called 10,000,000 times.
Can you imagine the huge difference in memory allocation? No? Let me show you…
Comparing Useless Method Call to No Call
On this section, I am going to show you the difference in performance and memory allocation between calling an empty/dummy/useless method to not calling it at all.
Check the code below:
What we can notice here:
The Calc method is actually doing nothing.
The RunSlow method calls the Calc method 10,000,000 times.
The RunFast method just loops 10,000,000 times.
So, running a Benchmark for these two methods would produce the following result.
See, the difference is huge. Therefore, it would be nice if we can avoid performing useless calls whenever we can.
How To Avoid Useless Calls?
We can do this using more than one approach and in the next sections we will try them one by one.
Using Properties
In this approach, we are going to define three boolean properties to represent the calls that each concrete implementation can provide. Then, we can use these properties to check if a call would be useful or not.
Now, we would update the code to be as follows.
TaxCalculator
Here we just added the following:
protected abstract bool CanCalculateExpenses { get; }
protected abstract bool CanCalculateIncomeTax { get; }
protected abstract bool CanCalculateVat { get; }
And then, we started using these three boolean properties inside the CalculateTaxes method to decide whether a call needs to be performed or not.
Now, I can hear someone saying:
Ok, but now you added extra lines of code to check the property. Wouldn’t that affect the performance?
My answer would be yes, it could. However, it might still be better than performing an empty/useless call.
Furthermore, I will show you something interesting at the end of the article so just bear with me.
TaxCalculator1 and TaxCalculator2
Here, we are just overriding the boolean properties to set them to the right values.
Console Application
Using Flagged Enums
In this approach, we are going to define a flagged enum to represent the calls that each concrete implementation can provide. Then, we can use this flagged enum to check if a call would be useful or not.
Note: If you want to learn about Flagged Enums in .NET C#, you can check this the article Flagged Enumerations: How To Represent Features Combinations Into One Field.
Now, we would update the code to be as follows.
TaxCalculationCapabilities
Here we defined the flagged enum TaxCalculationCapabilities.
TaxCalculator
Here we just added the following:
protected abstract TaxCalculationCapabilities TaxCalculationCapabilities
{
get;
}
And then, we started using this TaxCalculationCapabilities property inside the CalculateTaxes method to decide whether a call needs to be performed or not.
TaxCalculator1 and TaxCalculator2
Here, we are just overriding the TaxCalculationCapabilities property to set it to the right value.
Console Application
Using Interfaces
In this approach, we are going to split and abstract each of the three methods in its own interface. So, whenever a concrete implementation is capable of providing a reasonable implementation of any of these methods, it just needs to implement the corresponding interface.
Now, we can also remove the abstract definitions of these three methods from the base class as they are not needed anymore.
Furthermore, the base class would now check if the current concrete implementation implements each interface before calling the corresponding method.
This way, we fixed the two problems we mentioned before:
Useless/Dummy method calls.
Violating the Interface Segregation Principle of the SOL(I)D principles.
Now, we would update the code to be as follows.
ICalculateExpenses, ICalculateIncomeTax, and ICalculateVat
It’s clear, right?
TaxCalculator1 and TaxCalculator2
Now as you can see, the contracts are clear and simple. No more useless methods to implement.
TaxCalculator
Also here, the implementation is now clear and simple. Notice that we removed the definitions of the three abstract methods from here as they are not needed anymore and we don’t want to force the concrete implementations to implement them even when not actually needed.
Console Application
Are We Done?
I know you are still waiting for a reply on your argument:
Ok, but now you added extra lines of code to check the property. Wouldn’t that affect the performance?
I didn’t want to discuss this point earlier because I wanted then to drive your focus to the importance of not calling useless calls. However, now we can discuss this.
There is a fourth approach which we could follow to avoid the useless calls and also avoid the if condition checks. Getting interested? Let’s do it…
Using Expression Trees
The main idea behind this approach is to write some code which would generate the alternative CalculateTaxes method at runtime. Don’t get it? Let me explain.
Here is what we should think about:
Our optimal goal is to have an implementation of the CalculateTaxes method without any unneeded calls even the if condition checks.
But, we can’t do this as the implementation itself would be different for each concrete implementation.
Therefore, we can’t achieve this by defining the implementation of the CalculateTaxes method at design/development time.
But, what if the implementation of the CalculateTaxes method could change at runtime according to the concrete implementation?
In other words, as an example, what if the implementation of the CalculateTaxes method is automatically changed at runtime to the following for the TaxCalculator1 ?
decimal total = 1436;
total += DoSomething1(124);
total += incomeTaxCalculator.CalculateIncomeTax(452);
total += DoSomething2(624);
return total;
Also, what if the the implementation of the CalculateTaxes method is automatically changed at runtime to the following for the TaxCalculator2 ?
decimal total = 1436;
total += expensesCalculator.CalculateExpenses(1436);
total += DoSomething1(124);
total += DoSomething2(624);
return total;
Wouldn’t that be great? Indeed but how can we do this? We can do this using Expression Trees as follows. In this solution, we are going to use the Properties approach combined with Expression Trees.
TaxCalculator1 and TaxCalculator2
Nothing new here, it is the same implementation of the approach where we were using the Properties.
TaxCalculator
What we should notice here:
We defined the BuildExpression method. This is where the magic happens.
Inside this method we are using the Expression API to build the expression of the CalculateTaxes method that would be executed at runtime.
While doing this, we check the passed in TaxCalculator to know which methods calls should be added to the expression and which should be skipped.
This way the check itself would not be done inside the execution of the CalculateTaxes method which would happen for 10,000,000 times.
This means that calling this BuildExpression method, passing in a certain concrete implementation of TaxCalculator, would return a pure and clean implementation of the CalculateTaxes method for that concrete implementation.
This is what we are dreaming of.
We need also to know that compiling that generated expression into an actual Func<TaxCalculator, decimal> is too costy.
That’s why we need to do it once and for sure outside the CalculateTaxes method call.
And we already did that by defining the Func<TaxCalculator, decimal> field and setting it inside the constructor by calling the BuildExpression method.
Then inside the CalculateTaxes method we just call the Func<TaxCalculator, decimal> saved in the field.
Console Application
I can hear you now asking:
Should I always use this approach?
As you can see, the solution would be perfect for runtime performance but it is not that perfect in terms of readability and extensibility. It is not easy to read the code, understand it, and extend it.
Therefore, my recommendation would be to not use this approach unless the performance of a certain module is too critical to the extent that you accept to sacrifice your readability and extensibility.
Benchmarking
Now you might be asking:
What proves that your approaches are actually better?
Yes, I agree with you. You should not just trust me on it, you need some proof that it actually works and adds a value. And that’s what I did…
I ran a Benchmarking project as follows:
The first run yielded this result
And the second run yielded this result
So, as you can see, now you could imagine the difference between all the approaches.
Conclusions
Ok, we have discussed more than one approach but now we don’t know how to decide which approach to go with.
If you ask me, I would say:
I prefer to use the Interfaces solution as it is clean.
Don’t use the Expression Tree solution unless you really need it.
Prefer Composition to Inheritance. In my point of view, I think the same result could be achieved by Composition and it would be better in design and maintenance.
Always do your own realistic Benchmarking and Measuring. What could be the best for one solution could be the worst for another.
You should know that there should always be a tradeoff between memory allocation (space) and processing power. Therefore, be wise about it and choose what fits in your case.
Final Thoughts
In this article we discussed the Template Method Design Pattern in .NET C#.
We also discussed some of the challenges that you could encounter while working on your design and analyzed more than one approach on how to tackle these challenges.
Now you understand what the Template Method Design Pattern is about. However, this is not the end of the story.
You would need to search the internet for more articles and tutorials about the Template Method Design Pattern and its usages. This would help you understand it more.
Finally, I hope you found reading this article as interesting as I found writing it.
Comments