- Home>
- Design Patterns>
- Template method example
In a project I work on, I need to programmatically fill out some PDF forms. The forms are very similar in terms of structure, content, and fields’ names. Using iTextSharp library, I can go through the fields in the PDF and set the values. The logic for filling out a PDF form can be generic, as I just need to know what are the fields’ names and the corresponding values of the PDF. I come up with a generic model and common logic for filing out the form, as demonstrated in the below snippets.
public abstract class PDFService<T> where T : class { public abstract IDictionary<string, PdfFormFieldModel> ToFormDict(T formModel); public virtual Stream FillPdf(PDFForm<T> formFillingRequest) { // ...codes omitted for brevity. foreach (KeyValuePair<string, PdfFormFieldModel> entry in ToFormDict(formFillingRequest.Content as T)) { // use iTextSharp to loop through the fields and set the corresponding values. } } }
The PdfFormFieldModel is just a POCO to encapsulate a field in a PDF.
public class PdfFormFieldModel { public bool IsVisible { get; set; } public string Name { get; set; } public FieldType Type { get; set; } public string Value { get; set; } public PdfFormFieldModel(string fieldName) { Name = fieldName; } public PdfFormFieldModel(string fieldName, FieldType fieldType) { Name = fieldName; Type = fieldType; } public PdfFormFieldModel(string name, string value) : this(name, FieldType.STRING, value) { } public PdfFormFieldModel(string name, FieldType type, string value) : this(name, type) { Value = value; IsVisible = true; } }
For each of the two forms, I create a subclass in which I override the ToFormDictionary() method to return the dictionary of which the keys represent the fields’ names in the PDF and the values represent the values to set in those fields.
Below show example snippets of the subclasses.
public class FormAPdfService : PDFService<FormA> { public override IDictionary<string, PdfFormFieldModel> ToFormDict(FormA form) { IDictionary<string, PdfFormFieldModel> fieldDict = new Dictionary<string, PdfFormFieldModel>(); fieldDict.Add("Email", new PdfFormFieldModel("Email", form.Email)); // ... codes omitted for brevity return fieldDict; } } public class FormBPdfService : PDFService<FormB> { public override IDictionary<string, PdfFormFieldModel> ToFormDict(FormB form) { IDictionary<string, PdfFormFieldModel> fieldDict = new Dictionary<string, PdfFormFieldModel>(); fieldDict.Add("Name", new PdfFormFieldModel("Name", formB.Name)); // ... codes omitted for brevity return fieldDict; } }
I have followed the template method design pattern. The PDFService defines the FillPDF method, which is the template method. The abstract ToFormDict() method is for the subclasses to override to provide the data for the algorithm.
A template method defines an algorithm in terms of abstract operations that subclasses override to provide concrete behavior.
Gamma, E., Helm, R., Johnson, R. E., & Vlissides, J. (2016). TEMPLATE METHOD. In Design patterns: Elements of reusable object-oriented software (pp. 325–326). essay, Addison-Wesley.
Design Patterns: Elements of Reusable Object-Oriented Software
Notes on the three programming paradigms
More on inheritance
Notes on Barbara Liskov paper on data abstraction and hierarchy
The Liskov Substitution Principle
Common frameworks, libraries and design patterns I use
Notes on component coupling
Notes on Component Cohesion
Notes on The Clean Architecture