Continuing with the Design Patterns series, this post is about the Template Method design pattern. Template Method is a Behavioural design pattern which defers the exact steps of an algorithm to a subclass.
Problem Statement
Lets say we are building a data driven application. Like any other data driven business application, the end users of this system also needs to save data in different formats. The application should allow users to take a snapshot of the data and export it into their preferred format. We need to enable users to export data in PDF files or excel files. The contents of the file should contain a header, the report and the footer.
Solution without Template Method Design Pattern
Lets get started and build the required features. Assuming that we have fetched the data from respective data source, we can build one class for generating the PDF report and another one for generating the excel report. Lets get started with the PDF report writer.
public class PDFReportWriter
{
public void WriteHeader()
{
// writes header details into PDF document
}
public void WriteReportContents()
{
// writes report into PDF document
}
public void WriteFooter()
{
// writes footer details into PDF document
}
}
The PDFReportWriter class consists of 3 methods one for each operation like Header, Contents and Footer to be written into the PDF file. I am not going into the details of the implementation of these methods. In reality we can use some library like iTextSharp which provides an API for generating PDF documents. The details of how we can output the contents to a PDF file outside the scope of this article.
Similar to the PDFReportWriter we can define an ExcelReportWriter class which can generate a report and save it in Excel format. To save some space, I am not going to copy the contents of the ExcelReportWriter here. You can find the source code it in the attached solution file.
Lets see how the client code which depends on these classes can make use of them.
public class Client
{
public void GenerateReport(ReportType reportType)
{
if (reportType == ReportType.PDF)
{
PDFReportWriter pdfReportWriter = new PDFReportWriter();
pdfReportWriter.WriteHeader();
pdfReportWriter.WriteReportContents();
pdfReportWriter.WriteFooter();
}
if (reportType == ReportType.EXCEL)
{
ExcelReportWriter excelReportWriter = new ExcelReportWriter();
excelReportWriter.WriteHeader();
excelReportWriter.WriteReportContents();
excelReportWriter.WriteFooter();
}
}
}
We have a class named Client which has a method called GenerateReport. Depending on the report type we instantiate either PDFReportWriter or ExcelReportWriter and call the respective methods of the class. Note that I am not really worry about the object oriented features and code optimization here. We can use factory or abstract factory pattern here to remove the conditional and duplicated code. Since it is outside the scope of this I’ll leave it as is.
If we use this approach it will work. But there are minor flaws here which can cause problems. All the methods WriteHeader, WriteReportContents and WriteFooter are public methods of PDFReportWriter and ExcelReportWriter. If we look at the requirements, we should have the header first then the contents and finally the footer. Imagine what can happen if the developer who uses these classes calls the method in some other order.
Another case can be when some developer forgets to use one or more method. For example in the above client code for PDF report generation, the developer can completely forget to make a call pdfReportWriter.WriteFooter().
How can we make sure that the report is generated correctly with all the details? This is where the Template Method pattern comes into the picture. It defines the structure of an algorithm into a method and allows the subclasses to provide the specific implementations. Lets see how we can refactor our code to make use of Template Method Design Pattern.
Refactored Solution with Template Method Design Pattern
The steps of our algorithm in this case are the rendering of three blocks of the report. We want to ensure that three blocks Header, Contents and Footer are always rendered in the same order in both the reports. We define a abstract class which is used to define a method. This method defines the steps of the algorithm. Lets see how.
public abstract class ReportWriter
{
public void WriteReport()
{
WriteHeader();
WriteReportContents();
WriteFooter();
}
public abstract void WriteFooter();
public abstract void WriteReportContents();
public abstract void WriteHeader();
}
ReportWriter is an abstract class. I have encapsulated the report writing into a method called WriteReport and defined the steps of report generation inside this method. Both the PDFReportWriter and ExcelReportWriter inherit from this abstract class and override the methods. After this refactoring, the client code becomes very simple. We need to call only one method instead of calling individual methods.
public void GenerateReport(ReportType reportType)
{
ReportWriter reportWriter = null;
switch (reportType)
{
case ReportType.PDF:
reportWriter = new PDFReportWriter();
break;
case ReportType.EXCEL:
reportWriter = new ExcelReportWriter();
break;
}
if (reportWriter != null)
{
reportWriter.WriteReport();
}
}
A small refactoring makes the client code even more easier. What this refactoring has enabled us to do is modify the report generation without impacting the client code. Assume that in future, the business users decide to add grouping functionality to the report. We can define another abstract method in the abstract class and the derived classes can provide their own implementations of rending the grouped data.
Usage of Template Method within DotNet Framework
One of the most common example where Template Method is used within DotNet framework is the ASP.NET. The lifecycle of an ASP.NET Page follows a set of events. The application developer can override the events as per their needs to customize the behaviour of the page. The framework provides a default implementation for some of the events which can be overridden by developers.
Apart from the ASP.NET page lifecycle, the server controls also makes use of the Template Method pattern. If you have used data bound controls like DataGrid, Repeater etc you can customize various templates like HeaderTemplate, RowTemplate, FooterTemplate. Almost all server controls make use of the Template Method pattern. ASP.NET or other DotNet technologies like Winforms, Silverlight, WPF etc provide templates with default implementations.
One example of default implementation is the way ViewState is managed in the Webforms. The default implementation stores the viewstate data into a hidden field inside the html page that is rendered to the browser. We can override the LoadViewState and SaveViewState methods to store the view state into data base or the Web server.
We can also have scenarios where the default implementation does nothing. In such cases we can call the methods which are invoked from with the template method algorithm as hooks or placeholders. In the default implementation, they don’t do anything. But they can be overridden in the derived classes to provide custom behaviour. We can see examples of this in the Pre and Post processing events in the ASP.NET page life cycle.
Conclusion
The example I used here related to report writing is somewhat similar to the one used during Abstract Factory design pattern. One thing we need to understand while using design patterns is their intent. We might think couple of patterns are almost same in the way they behave by looking at the UML diagram. But the main point which helps us in pin pointing the pattern to use is the intent of the pattern.
In case of Template Method Design Pattern, the intent is to abstract the algorithm into a method and let the subclasses override the actual behaviour. Template method is used when we have certain steps common in different representations of an algorithm. It can also be used to provide “Hooks” or “placeholders” for operations which might not be necessary for all the different implementations.
As always the complete solution can be downloaded.
Until next time Happy Programming.
Further Reading
Here are some books I recommend related to the topics discussed in this bog post.
Good one Nileh
ReplyDeleteVery clear explanation..Nice
ReplyDelete