Real World Implementation Of The Template Method Pattern
A real world example could be a data parser that supports different formats. Consider a situation where your application needs to support parsing both JSON and XML data. The overall parsing process (loading data, parsing, validation and usage of the parsed data) remains the same, but the actual parsing differs based on the format.
This diagram represents the class hierarchy in your code. DataParser
is an
abstract class that provides the parseData
method, along with default
implementations for loadData
, validate
, and useData
methods. The parse
method is declared as protected and abstract, which means it needs to be
overridden by each subclass.
JSONParser
and XMLParser
are concrete classes that extend DataParser
. Each
of these classes provides its own implementation of the parse
method,
depending on the specific data format they are designed to handle (JSON or XML).
Real World Implementation
Here's how we could structure this using the Template Method pattern in TypeScript:
abstract class DataParser {
public parseData(): void {
this.loadData();
// This is a placeholder. In a real application,
// loadData would probably return the data.
const data = "sample data";
const parsedData = this.parse(data);
this.validate(parsedData);
this.useData(parsedData);
}
protected loadData(): void {
console.log("Loading data (could be from a file, database, etc.)");
}
protected abstract parse(data: string): any;
protected validate(data: any): void {
console.log("Validating the parsed data...");
}
protected useData(data: any): void {
console.log("Using the parsed data (could be displaying, storing, etc.)");
}
}
class JSONParser extends DataParser {
protected parse(data: string): any {
console.log("Parsing data as JSON...");
return JSON.parse(data);
}
}
class XMLParser extends DataParser {
protected parse(data: string): any {
console.log("Parsing data as XML...");
// here we assume a parseXML function exists that can parse XML data
// return parseXML(data);
}
}
function clientCode(dataParser: DataParser): void {
dataParser.parseData();
}
console.log("Parsing JSON data:");
clientCode(new JSONParser());
console.log("");
console.log("Parsing XML data:");
clientCode(new XMLParser());
In this code:
DataParser
is an abstract class that defines the template method (parseData()
) for the data parsing process.JSONParser
andXMLParser
are concrete classes that provide their own implementation of theparse()
method.The
clientCode()
function accepts an object ofDataParser
and calls itsparseData()
method. This allows the same client code to work with different parsers.
Advanatges Of The Template Method Pattern
Code Reusability and DRY (Don't Repeat Yourself) Principle
The Template Method pattern can help to avoid code duplication by pulling the duplicate code into a superclass.
In our example, the common steps (loading data, validating and using parsed
data) are implemented once in the superclass DataParser
, and the
subclasses (JSONParser
, XMLParser
) only need to implement the part that
differs (the parse
method).
abstract class DataParser {
public parseData(): void {
this.loadData();
// ...
this.validate(parsedData);
this.useData(parsedData);
}
protected loadData(): void {
console.log("Loading data...");
}
// ...
}
class JSONParser extends DataParser {
protected parse(data: string): any {
console.log("Parsing data as JSON...");
// ...
}
}
Interface Seggregation And Dependency Inversion
The client code needs to know only about the abstract class and doesn't need to be aware of the concrete classes. The client code remains the same irrespective of the number of subclasses.
function clientCode(dataParser: DataParser): void {
dataParser.parseData();
}
console.log("Parsing JSON data:");
clientCode(new JSONParser());
console.log("Parsing XML data:");
clientCode(new XMLParser());
Encapsulation of Complexities
The complex parts of the code or the algorithm are encapsulated in the superclass, providing a simpler interface to the client.
public parseData() : void {
this.loadData();
const data = "sample data";
const parsedData = this.parse(data);
this.validate(parsedData);
this.useData(parsedData);
}
Control over the Subclasses
The Template Method pattern offers a way to control subclasses by allowing them to add/alter some parts of an algorithm, but not the whole thing or the order of the steps.
abstract class DataParser {
// ...
protected abstract parse(data: string): any;
}
class JSONParser extends DataParser {
protected parse(data: string): any {
console.log("Parsing data as JSON...");
// ...
}
}
Extensibility
The Template Method pattern makes the code more
extensible. For instance, if you need to support a new data format (e.g.,
YAML), you can simply create a new subclass (YAMLParser
) and provide the
implementation for the parse
method. You don't need to change the client
code or the superclass.
class YAMLParser extends DataParser {
protected parse(data: string): any {
console.log("Parsing data as YAML...");
// ...
}
}
Each of these advantages helps to create more maintainable, understandable, and flexible code.
Time To Transition From JavaScript To TypeScript
Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.
SEE COURSE DETAILSWhat Can You Do Next 🙏😊
If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.