Template Delegate Pattern
This post was originally published here.
I’ve had to use this pattern a few times, most recently in Behave#. It’s similar to the Template Method pattern, but doesn’t resort to using subclassing for using a template method. Instead, a delegate is passed to the Template Method to substitute different logic for portions of the algorithm.
The pattern
Two methods in unrelated classes perform similar general algorithms, yet some parts of the algorithm are different.
Generalize the algorithm by extracting their steps into a new class, then extract methods for the specialized parts into delegates to be passed in to the new class.
Motivation
Helper methods tend to clutter a class with responsibilities orthogonal to its true purpose. Additionally, several methods in one class might need to perform the same algorithm in slightly different ways. While Template Method is concerned with removing behavioral duplication, algorithmic duplication can be just as rampant.
Removing algorithmic duplication through Template Method can make the code more obtuse, as often the abstraction of the template class doesn’t add any meaning to the system. Additionally, sometimes duplicate algorithms are in completely separate classes, and it would be impossible or unwise to try to extract a common subclass from the two classes. We can remove algorithmic duplication by consolidating the algorithm into its own method or class, and pass in parts of the algorithm that might vary.
Mechanics
- Use Extract Method to separate the purely algorithmic section of each method from any surrounding behavioral code.
- Compile and test.
- Decompose the methods in each algorithm so that all of the steps in the algorithm are identical or completely different.
- For each step in the algorithm that is completely different, use Extract Method to pull out the varying logic. Name these extracted methods the same.
- Compile and test.
- Optionally, use Introduce Parameter Object for each extracted algorithm method that does not match the same number of parameters for the other different algorithm steps. Compile and test.
- If there is not an existing delegate type that matches the extracted variant methods of the algorithm, create a delegate type that matches both extracted methods.
- Use Add Parameter and add a new parameter of the delegate type created earlier, and modify the algorithm to use the delegate method to execute the varying logic. Repeat for both algorithm methods. Each algorithm method should look exactly the same at this point, except for parameter and return types.
- Compile and test.
- Use Extract Class and Move Method to move the two algorithm methods to a new, generalized (and optionally generic) class. Modify the calling class to use the new class and methods.
- Compile and test.</ul>
Example
Suppose I find the two methods in different classes in a large codebase that do recursive searches. One finds a Control based on an ID, and the other searches for XmlNodes based on an attribute value:
private Control FindControl(ControlCollection controls) { foreach (Control control in controls) { if (control.ID == txtControlID.Text) return control;
- Compile and test.</ul>
- Use Extract Class and Move Method to move the two algorithm methods to a new, generalized (and optionally generic) class. Modify the calling class to use the new class and methods.
- Compile and test.
- Use Add Parameter and add a new parameter of the delegate type created earlier, and modify the algorithm to use the delegate method to execute the varying logic. Repeat for both algorithm methods. Each algorithm method should look exactly the same at this point, except for parameter and return types.
- If there is not an existing delegate type that matches the extracted variant methods of the algorithm, create a delegate type that matches both extracted methods.
- Optionally, use Introduce Parameter Object for each extracted algorithm method that does not match the same number of parameters for the other different algorithm steps. Compile and test.
- Compile and test.
- For each step in the algorithm that is completely different, use Extract Method to pull out the varying logic. Name these extracted methods the same.
return FindControl(control.Controls); } }
- Decompose the methods in each algorithm so that all of the steps in the algorithm are identical or completely different.
- Compile and test.
private XmlNode FindElement(XmlNodeList nodes) { foreach (XmlNode node in nodes) { if (node.Attributes[“ID”].Value == “4564”) return node;
<span class="kwrd">return</span> FindElement(node.ChildNodes);
} }
</pre> </div>
Both of these methods perform the exact same logic, a recursive search, but the details are slightly different.
In this example, the first step is already complete and each method contains only the algorithm I’m interested in. From looking at these methods, it looks like there are 3 distinct parts: the loop, the comparison, and the actual matching logic. The two differing parts I see in the algorithm are:
* Match
* Get the children based on the current item in the loop</ul>
I’ll apply Extract Method to pull out the varying logic and name these methods the same. Here are the extracted methods:
<div class="CodeFormatContainer">
<pre><span class="kwrd">private</span> <span class="kwrd">bool</span> IsMatch(Control control) {
<span class="kwrd">return</span> control.ID == txtControlID.Text; }
private bool IsMatch(XmlNode node) { return node.Attributes[“ID”].Value == “4564”; }
private ControlCollection GetChildren(Control control) { return control.Controls; }
private XmlNodeList GetChildren(XmlNode node) { return node.ChildNodes; } </pre> </div>
Note that the names of each method is the same, as well as the number of parameters. Now the original algorithm methods call these extracted varying methods:
<div class="CodeFormatContainer">
<pre><span class="kwrd">private</span> Control FindControl(ControlCollection controls) {
<span class="kwrd">foreach</span> (Control control <span class="kwrd">in</span> controls)
{
<span class="kwrd">if</span> (IsMatch(control))
<span class="kwrd">return</span> control;
<span class="kwrd">return</span> FindControl(GetChildren(control));
} }
private XmlNode FindElement(XmlNodeList nodes) { foreach (XmlNode node in nodes) { if (IsMatch(node)) return node;
<span class="kwrd">return</span> FindElement(GetChildren(node));
} }
</pre> </div>
These algorithm methods are starting to look very similar. Next, I need to find a delegate type to represent the varying methods, namely “IsMatch” and “GetChildren”. Since I’m working with Visual Studio 2008, some good candidates already exist with the Func delegate types. I like these delegate types as they are generic and may lend to some better algorithm definitions in the future, so I’ll stick with Func. Here’s the FindControl method after I use Add Parameter to pass in the varying algorithm logic:
<div class="CodeFormatContainer">
<pre><span class="kwrd">private</span> Control FindControl(IEnumerable<Control> controls,
Func<Control, <span class="kwrd">bool</span>> predicate,
Func<Control, IEnumerable<Control>> childrenSelector) {
<span class="kwrd">foreach</span> (Control control <span class="kwrd">in</span> controls)
{
<span class="kwrd">if</span> (predicate(control))
<span class="kwrd">return</span> control;
<span class="kwrd">return</span> FindControl(childrenSelector(control), predicate, childrenSelector);
} }
</pre> </div>
I changed the type of the “controls” parameter to IEnumerable<Control> from ControlCollection, to reduce the number of types seen in the algorithm method. I change the client code of this algorithm to pass in the new parameters, compile and test:
<div class="CodeFormatContainer">
<pre><span class="kwrd">private</span> <span class="kwrd">void</span> SetLabelText() {
<span class="kwrd">string</span> text = txtLabelText.Text;
<span class="kwrd">string</span> controlID = txtControlID.Text;
<span class="kwrd">if</span> (<span class="kwrd">string</span>.IsNullOrEmpty(text) || <span class="kwrd">string</span>.IsNullOrEmpty(controlID))
<span class="kwrd">return</span>;
Control control = FindControl(page.Controls, IsMatch, GetChildren); }
</pre> </div>
Note that the “IsMatch” and “GetChildren” are method names in this class, so I’m passing the matching and children algorithms to the FindControl method for execution when needed. Finally, I use Extract Class and Move Method to move the two virtually identical algorithm methods to a single method on a new class. Here’s the final result, with some minor changes to use [extension methods](http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx):
<div class="CodeFormatContainer">
<pre><span class="kwrd">public</span> <span class="kwrd">static</span> T RecursiveSearch<T>(<span class="kwrd">this</span> IEnumerable<T> items,
Func<T, <span class="kwrd">bool</span>> predicate,
Func<T, IEnumerable<T>> childrenSelector) {
<span class="kwrd">foreach</span> (T item <span class="kwrd">in</span> items)
{
<span class="kwrd">if</span> (predicate(item))
<span class="kwrd">return</span> item;
<span class="kwrd">return</span> RecursiveSearch(childrenSelector(item), predicate, childrenSelector);
}
<span class="kwrd">return</span> <span class="kwrd">default</span>(T); }
</pre> </div>
<div class="CodeFormatContainer">
</div>
I’ve made the method generic, as the only final variant between the extracted algorithm methods was the type of the item I was finding. By making the method generic, the return type is strongly typed to the item I’m searching for. The final client code doesn’t look too much different from earlier:
<div class="CodeFormatContainer">
<pre><span class="kwrd">private</span> <span class="kwrd">void</span> SetLabelText() {
<span class="kwrd">string</span> text = txtLabelText.Text;
<span class="kwrd">string</span> controlID = txtControlID.Text;
<span class="kwrd">if</span> (<span class="kwrd">string</span>.IsNullOrEmpty(text) || <span class="kwrd">string</span>.IsNullOrEmpty(controlID))
<span class="kwrd">return</span>;
Control control = page.Controls.Cast<Control>().RecursiveSearch(IsMatch, GetChildren); }
</pre> </div>
The client code for the “FindElement” looks exactly the same, and now there is no duplication of the recursive search logic. I’ve extracted the varying logic into methods which can be passed in as delegates to the new, generic extracted algorithm. Since I’m using C# 3.0, I can go as far as using [lambda expressions](http://weblogs.asp.net/scottgu/archive/2007/04/08/new-orcas-language-feature-lambda-expressions.aspx) instead of the extracted “IsMatch” and “GetChildren” methods.
#### Conclusion
The Template Delegate pattern is used quite extensively with the new [Enumerable extension methods](http://msdn2.microsoft.com/en-us/library/system.linq.enumerable(VS.90).aspx) (such as the [Where](http://msdn2.microsoft.com/en-us/library/bb534803(VS.90).aspx) method). With delegate creation in C# 3.0 becoming much simpler with [lambda expressions](http://weblogs.asp.net/scottgu/archive/2007/04/08/new-orcas-language-feature-lambda-expressions.aspx), it’s becoming easier to compose generalized algorithms where varying portions are passed in as delegate parameters. By using the Template Delegate pattern, I can reduce the number of junk algorithmic methods into a single generic class that serves the needs of current and future client code.