Skip to main content

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:

  1. DataParser is an abstract class that defines the template method (parseData()) for the data parsing process.

  2. JSONParser and XMLParser are concrete classes that provide their own implementation of the parse() method.

  3. The clientCode() function accepts an object of DataParser and calls its parseData() 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.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

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 DETAILS

What 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.

YouTube @cloudaffle