Builder Pattern: Pizza Order
Problem
Each input line is a pizza order made of options in any order: size:NAME (required), cheese (extra cheese) and top:NAME (a topping, can be repeated). Print the description: NAME pizza, and if there are extras, with and the list separated by comma and space, where extra cheese comes first and then the toppings in alphabetical order. If the size is missing print error: size required. Use the Builder pattern.
Constraints
- 1 <= lines <= 10^4
Examples
Input
size:large cheese top:ham top:olives top:ham
Output
large pizza with extra cheese, ham, olives error: size required
Solutions
A builder is useful when an object has many optional parts. The setters can be called in any order, and the one place that checks the rules is build(). The finished object is never half-ready.
/*
* Problem : Process lines.
* Input : Several lines.
* Output : One description per order.
* Approach: A builder with checks.
* Time : O(total options)
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: The Pizza is built by a Builder. The Builder collects the options step by step and checks them in build().
static final class Pizza {
private final String size;
private final boolean cheese;
private final java.util.List<String> toppings;
private Pizza(Builder builder) {
this.size = builder.size;
this.cheese = builder.cheese;
this.toppings = builder.toppings;
}
static class Builder {
private String size = null;
private boolean cheese = false;
private final java.util.List<String> toppings = new java.util.ArrayList<>();
// Step 2: Each setter returns the builder itself, so calls can be chained.
Builder size(String value) {
this.size = value;
return this;
}
Builder extraCheese() {
this.cheese = true;
return this;
}
Builder topping(String value) {
this.toppings.add(value);
return this;
}
// Step 3: build() checks the rule "size is required" and makes the final object.
Pizza build() {
if (size == null) {
throw new IllegalStateException("size required");
}
return new Pizza(this);
}
}
@Override
public String toString() {
java.util.List<String> parts = new java.util.ArrayList<>();
if (cheese) {
parts.add("extra cheese");
}
java.util.List<String> sorted = new java.util.ArrayList<>(toppings);
java.util.Collections.sort(sorted);
parts.addAll(sorted);
return size + " pizza" + (parts.isEmpty() ? "" : " with " + String.join(", ", parts));
}
}
// 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++) {
// Step 4: Read the options of the line and give each one to the builder.
Pizza.Builder builder = new Pizza.Builder();
for (String option : lines[i].split(" ")) {
if (option.startsWith("size:")) {
builder.size(option.substring(5));
} else if (option.equals("cheese")) {
builder.extraCheese();
} else if (option.startsWith("top:")) {
builder.topping(option.substring(4));
}
}
// Step 5: build() throws an exception if the size is missing. We print an error message then.
try {
output[i] = builder.build().toString();
} catch (IllegalStateException error) {
output[i] = "error: " + error.getMessage();
}
}
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)
- Where is the rule about the required size checked?