Introduction
The Template Method pattern is a behavioral design pattern from the classic Gang of Four Design Patterns book. In this article, I'll be offering a way to conceptualize and implement this pattern in C#, citing framework design as a quite familiar example. This pattern is really useful in scenarios where different methods need to be called in a specific sequence in order to work correctly. We don't want to leave that kind of responsibility to the consumer of our code. We want them to be able to pull up their code completion and use what's available to them without fear.
For implementing this pattern, we will be designing a simple Read, Evaluate, Print Loop (REPL) framework for command-line applications. I'm using this example because we actually see this design pattern all the time in our regular day-to-day life. We're just typically using this pattern as consumers of some framework.
The goal of our project is to allow the consumer of our framework, the application developer, to describe commands and actions for the user to perform. The Loop itself will just happen for them automatically. (The application developer should write code to describe what happens in their application, not focus on how it happens.)
The sign that you're using good abstractions, is when your code describes what happens, and not how it happens.
The Template Method design pattern is sometimes referred to as the Hollywood Principle, or "Don't call us, we'll call you!" This is because the consumer of our framework will not be responsible for invoking vital methods to make our application work correctly--but rather overriding methods that will be invoked behind the scenes.
Example Xamarin Forms
[Android sub-project, MainActivity.cs]
protected override void OnCreate(Bundle savedInstanceState)
{
// ...
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
// ...
}
[Xamarin.Forms shared project, App.xaml.cs]
public partial class App : Application
{
public App()
{
// ...
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
You can recognize the template methods by their "On" prefix. This convention signifies that these methods are triggered automatically when the appropriate events occur in the application. This type of code is very familiar because it's used everywhere in framework design. But have you ever wondered how to implement this yourself?
Let's do it.
Implementation
Our framework will consist of a single Application base class that the consumer can inherit from to describe their application.
public abstract class Application
{
We create it as an abstract class because we can't allow this class to be instantiated. It needs to be inherited, for this pattern to work.
We are not using an interface since we will be providing behaviors in this class.
First, we'll define a flag that we'll use later to quit the loop.
public abstract class Application
{
private bool _running = false;
(We'll set this to true later when our Application starts.)
Next, we will declare our template methods.
public abstract class Application
{
private bool _running = false;
protected abstract void OnStart();
protected abstract void OnExit();
protected abstract void OnInput(string input);
}
In these methods, the consumer can define what happens in their app, instead of how it happens. As the framework developer, we are responsible for invoking those methods.
Now we will begin to implement the behavior of our framework.
public void Run()
{
_running = true;
OnStart(); // This is where the "We'll call you" part of the Hollywood Principle comes in.
}
Note. This Run() method is not virtual or abstract because we can't allow this method to be overridden. This method will be invoked in Main() and start our loop.
Now we can finally implement the loop itself. The stages are.
Gather input from user
Let the app developer evaluate the input in their method
Repeat until quit
public void Run()
{
_running = true;
OnStart();
while (_running)
{
Console.Write("> ");
string input = Console.ReadLine();
OnInput(input);
}
OnExit();
}
Take a moment to really read this code and conceptualize what's happening. The app developer will have the user input handed to them by the mysterious inner workings of our framework. They only need to parse the input and decide what to do from there.
Note. You could also consider adding input validation and character escaping in this loop before passing the input to OnInput().
Finally, we will provide a way for the consumer of our framework to quit the application, thus triggering OnExit().
public void Quit()
{
_running = false;
}
Now our application framework is complete. Here's the final class in its entirety.
public abstract class Application
{
private bool _running = false;
protected abstract void OnStart();
protected abstract void OnExit();
protected abstract void OnInput(string input);
public void Run()
{
_running = true;
OnStart();
while (_running)
{
Console.Write("> ");
string input = Console.ReadLine();
OnInput(input);
}
OnExit();
}
public void Quit()
{
_running = false;
}
}
Here's an example app created using the framework. It only has a .quit command and a default error message for unrecognized commands.
public sealed class MyApp : Application
{
protected override void OnStart()
{
Console.WriteLine("~ Welcome to REPLFramework C#! ~");
Console.WriteLine("Type '.quit' to exit.");
}
protected override void OnInput(string input)
{
switch (input)
{
case ".quit":
Quit();
return;
default:
Console.WriteLine($"'{input}' is not a recognized command.");
break;
}
}
protected override void OnExit()
{
Console.WriteLine("See you again soon!");
}
}
The structure of that code looks exactly like the Xamarin example earlier.
The only thing that needs to be done now is firing off the Run method in Main().
class Program
{
static void Main(string[] args)
{
var app = new MyApp();
app.Run();
}
}
Here's what it looks like when run.
Side Note
In a typical framework scenario, you would.
- Build the Application class as a package.
- Create a Visual Studio project template that generates the MyApp class, and the code for Main().
Conclusion
That's all there is to it! Hopefully, this article helped you understand this design pattern more thoroughly. Apply this pattern to your own code and create more effective abstractions today!
Thank you for reading!