Behavioral Design Patterns
Imagine you are building a tool to process different types of data files, like CSV and PDF files. The overall workflow for processing any file is the same:
Steps 1 and 4 (opening and closing the file) are identical for all file types. However, steps 2 and 3 (parsing and processing) are completely different for a CSV file versus a PDF file.
Without a good pattern, you might end up with duplicated code in each processor class:
public class CsvProcessor {
public void processFile() {
System.out.println("Opening file..."); // Common logic
System.out.println("Parsing CSV data..."); // Specific logic
System.out.println("Processing data from CSV..."); // Specific logic
System.out.println("Closing file..."); // Common logic
}
}
public class PdfProcessor {
public void processFile() {
System.out.println("Opening file..."); // DUPLICATE
System.out.println("Parsing PDF data..."); // Specific logic
System.out.println("Processing data from PDF..."); // Specific logic
System.out.println("Closing file..."); // DUPLICATE
}
}
This violates the DRY (Don't Repeat Yourself) principle. If you need to add logging to the Opening file... step, you have to remember to change it in both classes, which is error-prone and hard to maintain.
The Template Method is a behavioral design pattern that defines the skeleton of an algorithm in a superclass but lets subclasses override specific steps of the algorithm without changing its structure.
The core idea is to create a "template method" in a base class. This method defines the sequence of steps for an algorithm. Some of these steps are implemented directly in the base class (the parts that are common to all subclasses), while other steps are declared as abstract, forcing subclasses to provide their own implementation for those specific, variant parts.
templateMethod(), which contains the skeleton of the algorithm. It also defines the abstract "primitive operations" that subclasses must implement. It can also contain "hooks" which are methods with a default (but overridable) implementation.Let's refactor our data processing tool using the Template Method pattern.
// The Abstract Class defining the template method
public abstract class DataProcessor {
// This is the Template Method. It is final to prevent subclasses from changing the algorithm's structure.
public final void processFile(String filePath) {
openFile(filePath);
parseData();
processParsedData();
closeFile();
}
// Common steps implemented in the base class
private void openFile(String filePath) {
System.out.println("Opening file: " + filePath);
}
private void closeFile() {
System.out.println("Closing file.");
}
// Abstract "primitive operations" that subclasses MUST implement
protected abstract void parseData();
protected abstract void processParsedData();
}
// A Concrete Class for processing CSV files
public class CsvDataProcessor extends DataProcessor {
@Override
protected void parseData() {
System.out.println("Parsing data from CSV file...");
}
@Override
protected void processParsedData() {
System.out.println("Processing the parsed CSV data...");
}
}
// A Concrete Class for processing PDF files
public class PdfDataProcessor extends DataProcessor {
@Override
protected void parseData() {
System.out.println("Extracting text from PDF file...");
}
@Override
protected void processParsedData() {
System.out.println("Processing the extracted PDF text...");
}
}
// The Client
public class Client {
public static void main(String[] args) {
System.out.println("--- Processing a CSV file ---");
DataProcessor csvProcessor = new CsvDataProcessor();
csvProcessor.processFile("data.csv");
System.out.println("\n--- Processing a PDF file ---");
DataProcessor pdfProcessor = new PdfDataProcessor();
pdfProcessor.processFile("report.pdf");
}
}
Benefits:
final), you can ensure that all subclasses follow the same high-level sequence of steps, preventing them from altering the overall workflow.Drawbacks:
BaseWebService with a handleRequest() template), and the application developer's job is to create concrete subclasses that "fill in the blanks" with application-specific logic.