State Pattern: Order Workflow

Problem

An order starts in state new. Commands: pay (new -> paid), ship (paid -> shipped), deliver (shipped -> delivered) and cancel (new or paid -> cancelled). For a valid command print state: followed by the new state. For any other command print invalid <command> in <state> and keep the state. Model each state as its own class that knows which commands it accepts.

Constraints

  • 1 <= commands <= 10^4

Examples

Input

pay
ship
deliver

Output

state: paid
state: shipped
state: delivered

Input

ship

Output

invalid ship in new

Solutions

Best approachTime O(n)Space O(1)

In the State pattern the behaviour that depends on the state moves into one class per state. Adding a new state means adding a class, and the order code does not change. A plain table of transitions is shorter, but the classes can also hold extra behaviour per state.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : One result per command.
 * Approach: Each state is a class.
 * Time    : O(n)
 * Space   : O(1)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: Every state class knows its name and the next state for each command. null means "not allowed".
        interface State {
            String name();

            State next(String command);
        }

        static class Delivered implements State {
            public String name() {
                return "delivered";
            }

            public State next(String command) {
                return null;
            }
        }

        static class Cancelled implements State {
            public String name() {
                return "cancelled";
            }

            public State next(String command) {
                return null;
            }
        }

        static class Shipped implements State {
            public String name() {
                return "shipped";
            }

            public State next(String command) {
                return command.equals("deliver") ? new Delivered() : null;
            }
        }

        static class Paid implements State {
            public String name() {
                return "paid";
            }

            public State next(String command) {
                if (command.equals("ship")) {
                    return new Shipped();
                }
                return command.equals("cancel") ? new Cancelled() : null;
            }
        }

        static class New implements State {
            public String name() {
                return "new";
            }

            public State next(String command) {
                if (command.equals("pay")) {
                    return new Paid();
                }
                return command.equals("cancel") ? new Cancelled() : null;
            }
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        // Step 2: The order only keeps the current state object. It has no if-else chain about states.
        State state = new New();
        String[] output = new String[lines.length];
        for (int i = 0; i < lines.length; i++) {
            State following = state.next(lines[i]);
            if (following == null) {
                output[i] = "invalid " + lines[i] + " in " + state.name();
            } else {
                state = following;
                output[i] = "state: " + state.name();
            }
        }
        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. When is a transition table better than state classes?