We can combine the Template Method and Factory Method design patterns to achieve flexibility and extensibility in our code. The Template Method pattern is used when we want to define the skeleton of an algorithm in base class while allowing derived classes to override some specific steps of the algorithm. The Factory Method pattern is used when we want to delegate the responsibility of creating objects to the derived classes, allowing us to create objects of different types at runtime.
Let’s say we are building a framework to process different types of documents (e.g., PDF, Word, Excel, etc.). We want to define a base class for all types of documents, while allowing derived classes to override specific steps of the document processing algorithm. Additionally, we want to be able to create different types of documents at runtime, so we will use the Factory Method pattern.
Here’s an example implementation:
public abstract class DocumentProcessor {
// Template Method pattern
public final void processDocument() {
openDocument();
readDocument();
parseDocument();
// Hook method
if (shouldCompressDocument()) {
compressDocument();
}
writeDocument();
closeDocument();
}
private void openDocument() {
// Default implementation
}
protected abstract void readDocument();
protected abstract void parseDocument();
// Hook method
protected boolean shouldCompressDocument() {
return false; // Default implementation
}
private void compressDocument() {
// Default implementation
}
protected abstract void writeDocument();
private void closeDocument() {
// Default implementation
}
// Factory Method pattern
public static DocumentProcessor createDocumentProcessor(String documentType) {
switch (documentType) {
case "PDF":
return new PdfDocumentProcessor();
case "Word":
return new WordDocumentProcessor();
case "Excel":
return new ExcelDocumentProcessor();
// Add cases for other document types here
default:
throw new IllegalArgumentException("Invalid document type: " + documentType);
}
}
}
public class PdfDocumentProcessor extends DocumentProcessor {
protected void readDocument() {
// Implementation for reading a PDF document
}
protected void parseDocument() {
// Implementation for parsing a PDF document
}
protected void writeDocument() {
// Implementation for writing a PDF document
}
}
public class WordDocumentProcessor extends DocumentProcessor {
protected void readDocument() {
// Implementation for reading a Word document
}
protected void parseDocument() {
// Implementation for parsing a Word document
}
protected void writeDocument() {
// Implementation for writing a Word document
}
}
public class ExcelDocumentProcessor extends DocumentProcessor {
protected void readDocument() {
// Implementation for reading an Excel document
}
protected void parseDocument() {
// Implementation for parsing an Excel document
}
protected void writeDocument() {
// Implementation for writing an Excel document
}
// Override Hook method
protected boolean shouldCompressDocument() {
return true;
}
}
In this example, we have defined a ‘DocumentProcessor‘ base class that implements the document processing algorithm using the Template Method pattern. The ‘openDocument()‘, ‘compressDocument()‘, and ‘closeDocument()‘ methods have default implementations, while the ‘readDocument()‘, ‘parseDocument()‘, and ‘writeDocument()‘ methods are abstract and must be implemented by derived classes.
We have also defined a ‘shouldCompressDocument()‘ hook method that can be optionally overridden by derived classes to modify the behavior of the algorithm. In the ‘ExcelDocumentProcessor‘ class, we have overridden this hook method to enable document compression.
Finally, we have created a ‘createDocumentProcessor()‘ factory method in the ‘DocumentProcessor‘ class that returns an instance of a specific document processor based on the provided document type. This method can be extended to add support for additional document types.
Using this implementation, we can create different types of document processors at runtime:
DocumentProcessor pdfProcessor = DocumentProcessor.createDocumentProcessor("PDF");
pdfProcessor.processDocument();
DocumentProcessor wordProcessor = DocumentProcessor.createDocumentProcessor("Word");
wordProcessor.processDocument();
DocumentProcessor excelProcessor = DocumentProcessor.createDocumentProcessor("Excel");
excelProcessor.processDocument();
This provides us with the flexibility and extensibility we need, as we can add new document types and override specific steps of the document processing algorithm as needed.