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

Best approachTime O(total options)Space O(n)

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.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * 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)
  1. Where is the rule about the required size checked?