Template Method: Report Generator
Problem
A report always has three steps in the same order: header, row and footer. The base class has a method generate(name) that fixes this order, and the subclasses fill in the steps. CSV report: header id,name; row 1,<name>; footer end. HTML report: header <table>; row <tr><name></tr>; footer </table>. Text report: header # report; row - <name>; footer --. Every input line is: kind name. Print header: ... | row: ... | footer: ... for each line.
Constraints
- 1 <= lines <= 10^4
- names have no spaces
Examples
Input
csv amit
Output
header: id,name | row: 1,amit | footer: end
Solutions
The Template Method pattern keeps the fixed order of an algorithm in the base class and lets subclasses change only the steps. In Java the template method is marked final, so a subclass cannot break the order by mistake. The pattern is the reason frameworks call your code ('do not call us, we call you').
/*
* Problem : Process lines.
* Input : Several lines.
* Output : One report line per input line.
* Approach: Subclasses fill in the steps.
* Time : O(n)
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: The template method generate() is final: it fixes the order of the steps. The subclasses cannot change it.
static abstract class Report {
final String generate(String name) {
return "header: " + header() + " | row: " + row(name) + " | footer: " + footer();
}
abstract String header();
abstract String row(String name);
abstract String footer();
}
// Step 2: Each subclass fills in the steps (the "hooks") for its own format.
static class CsvReport extends Report {
String header() {
return "id,name";
}
String row(String name) {
return "1," + name;
}
String footer() {
return "end";
}
}
static class HtmlReport extends Report {
String header() {
return "<table>";
}
String row(String name) {
return "<tr>" + name + "</tr>";
}
String footer() {
return "</table>";
}
}
static class TextReport extends Report {
String header() {
return "# report";
}
String row(String name) {
return "- " + name;
}
String footer() {
return "--";
}
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
String[] output = new String[lines.length];
for (int i = 0; i < lines.length; i++) {
String[] part = lines[i].split(" ");
Report report = part[0].equals("csv") ? new CsvReport() : part[0].equals("html") ? new HtmlReport() : new TextReport();
// Step 3: The caller only calls the template method.
output[i] = report.generate(part[1]);
}
return output;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (1)
- Why is generate() marked final in Java?