{"id":"text-ui-bigger-program","slug":"bigger-program","title":"Planning a whole program","text":"You've now seen every piece of a program with a text user interface. When you write one from scratch, this order keeps each step small:\n\n1. Write down the commands and what each one prints, best as a short example conversation.\n2. Decide what the program must remember between commands. Those are the instance variables of the logic class.\n3. For each command, decide what the logic must do or answer. Those are its public methods: parameters for what comes in, return values for what goes out, and no printing.\n4. Write the logic class, and try it with a few calls in `main`, before there's any user interface.\n5. Write the `UserInterface` class: a constructor that takes the Scanner and the logic object, and `start()` with the command loop, one branch per command.\n6. Make `main` create the objects and call `start()`.\n\nHere are the first four for a coat check at a theater. Guests leave their coat and get a ticket with the number of its hook, and the ticket gets them their coat back:\n\n| Command | What it prints |\n| --- | --- |\n| `leave`, then a name | `Ana, your ticket is 1` |\n| `collect`, then a ticket number | `Here's your coat, Ana`, or `No coat on hook 7` |\n| `coats` | `Coats: 2` |\n| `quit` | `Good night` |\n\nThe coat check remembers whose coat is on which hook. A list of names does that: the hook's number is one more than its index, and a hook that's free again holds `null`. A new coat goes on the first free hook, or on a new hook at the end. Before writing any user interface, `main` tries the class out:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        CoatCheck check = new CoatCheck();\n        int ana = check.leave(\"Ana\");\n        int ben = check.leave(\"Ben\");\n        System.out.println(ana + \" \" + ben + \" \" + check.coats());\n        System.out.println(check.collect(ana));\n        System.out.println(check.collect(ana));\n        System.out.println(check.leave(\"Cleo\") + \" \" + check.coats());\n    }\n}\n// ==== CoatCheck.java ====\nimport java.util.ArrayList;\n\npublic class CoatCheck {\n    private ArrayList<String> hooks;\n\n    public CoatCheck() {\n        this.hooks = new ArrayList<>();\n    }\n\n    public int leave(String owner) {\n        for (int i = 0; i < this.hooks.size(); i++) {\n            if (this.hooks.get(i) == null) {\n                this.hooks.set(i, owner);\n                return i + 1;\n            }\n        }\n        this.hooks.add(owner);\n        return this.hooks.size();\n    }\n\n    public String collect(int ticket) {\n        if (ticket < 1 || ticket > this.hooks.size()) {\n            return null;\n        }\n        String owner = this.hooks.get(ticket - 1);\n        this.hooks.set(ticket - 1, null);\n        return owner;\n    }\n\n    public int coats() {\n        int count = 0;\n        for (String owner : this.hooks) {\n            if (owner != null) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n```\n\n```output\n1 2 2\nAna\nnull\n1 2\n```\n\n- `leave` returns the ticket number, and `collect` returns the owner's name. The logic answers with values, and the user interface will turn them into sentences.\n- `collect` returns `null` when there's no coat on that hook, or no such hook at all. That's the logic's way of saying \"no coat\". Printing `No coat on hook 7` is up to the user interface.\n- After Ana collects her coat, her hook is free: the second `collect(ana)` finds it empty and returns `null`. Cleo's coat then goes on hook 1, the first free one.\n- These few lines already show that the logic works. If the whole program then misbehaves, the mistake is most likely in the user interface.\n\nThis step ends the MOOC's section 6.2. The next module is about testing: checking automatically that a logic class such as `CoatCheck` gives the right answers.\n","task":"Steps 5 and 6 for the coat check. `CoatCheck.java` is the class above, and `Main.java` is ready. Write `UserInterface.java`: its constructor takes a `Scanner` and a `CoatCheck`, and `start()` runs the commands of the table, with the prompt `> ` before each one. Anything else prints `Unknown command: ` and the command. For the input shown, the program prints:\n\n```\n> Ana, your ticket is 1\n> Ben, your ticket is 2\n> Coats: 2\n> Here's your coat, Ana\n> No coat on hook 1\n> Cleo, your ticket is 1\n> No coat on hook 7\n> Coats: 2\n> Good night\n```\n","kind":"code","seed":"// ==== UserInterface.java ====\nimport java.util.Scanner;\n\npublic class UserInterface {\n    // Write the instance variables, the constructor and start() here.\n}\n\n// ==== CoatCheck.java ====\nimport java.util.ArrayList;\n\npublic class CoatCheck {\n    private ArrayList<String> hooks;\n\n    public CoatCheck() {\n        this.hooks = new ArrayList<>();\n    }\n\n    public int leave(String owner) {\n        for (int i = 0; i < this.hooks.size(); i++) {\n            if (this.hooks.get(i) == null) {\n                this.hooks.set(i, owner);\n                return i + 1;\n            }\n        }\n        this.hooks.add(owner);\n        return this.hooks.size();\n    }\n\n    public String collect(int ticket) {\n        if (ticket < 1 || ticket > this.hooks.size()) {\n            return null;\n        }\n        String owner = this.hooks.get(ticket - 1);\n        this.hooks.set(ticket - 1, null);\n        return owner;\n    }\n\n    public int coats() {\n        int count = 0;\n        for (String owner : this.hooks) {\n            if (owner != null) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        CoatCheck check = new CoatCheck();\n        UserInterface ui = new UserInterface(scanner, check);\n        ui.start();\n    }\n}\n","solution":"// ==== UserInterface.java ====\nimport java.util.Scanner;\n\npublic class UserInterface {\n    // Write the instance variables, the constructor and start() here.\n    private Scanner scanner;\n    private CoatCheck check;\n\n    public UserInterface(Scanner scanner, CoatCheck check) {\n        this.scanner = scanner;\n        this.check = check;\n    }\n\n    public void start() {\n        while (true) {\n            System.out.print(\"> \");\n            String command = this.scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Good night\");\n                break;\n            }\n            if (command.equals(\"leave\")) {\n                String name = this.scanner.nextLine();\n                int ticket = this.check.leave(name);\n                System.out.println(name + \", your ticket is \" + ticket);\n            } else if (command.equals(\"collect\")) {\n                int ticket = Integer.valueOf(this.scanner.nextLine());\n                String owner = this.check.collect(ticket);\n                if (owner == null) {\n                    System.out.println(\"No coat on hook \" + ticket);\n                } else {\n                    System.out.println(\"Here's your coat, \" + owner);\n                }\n            } else if (command.equals(\"coats\")) {\n                System.out.println(\"Coats: \" + this.check.coats());\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n\n// ==== CoatCheck.java ====\nimport java.util.ArrayList;\n\npublic class CoatCheck {\n    private ArrayList<String> hooks;\n\n    public CoatCheck() {\n        this.hooks = new ArrayList<>();\n    }\n\n    public int leave(String owner) {\n        for (int i = 0; i < this.hooks.size(); i++) {\n            if (this.hooks.get(i) == null) {\n                this.hooks.set(i, owner);\n                return i + 1;\n            }\n        }\n        this.hooks.add(owner);\n        return this.hooks.size();\n    }\n\n    public String collect(int ticket) {\n        if (ticket < 1 || ticket > this.hooks.size()) {\n            return null;\n        }\n        String owner = this.hooks.get(ticket - 1);\n        this.hooks.set(ticket - 1, null);\n        return owner;\n    }\n\n    public int coats() {\n        int count = 0;\n        for (String owner : this.hooks) {\n            if (owner != null) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        CoatCheck check = new CoatCheck();\n        UserInterface ui = new UserInterface(scanner, check);\n        ui.start();\n    }\n}\n","hints":["The user interface keeps a `Scanner` and a `CoatCheck`, set in the constructor. For `leave`, read the name, keep the ticket that `leave` returns, and print both.","For `collect`, read the number with `Integer.valueOf(this.scanner.nextLine())`, keep what `collect` returns in a String variable, and check `if (owner == null)` to choose the message."],"tests":[{"name":"Test 1","stdin":"leave\nAna\nleave\nBen\ncoats\ncollect\n1\ncollect\n1\nleave\nCleo\ncollect\n7\ncoats\nquit\n","expect":"> Ana, your ticket is 1\n> Ben, your ticket is 2\n> Coats: 2\n> Here's your coat, Ana\n> No coat on hook 1\n> Cleo, your ticket is 1\n> No coat on hook 7\n> Coats: 2\n> Good night","hidden":false},{"name":"Test 2","stdin":"coats\ncollect\n0\nleave\nDev\nhang\ncollect\n1\ncoats\nquit\n","expect":"> Coats: 0\n> No coat on hook 0\n> Dev, your ticket is 1\n> Unknown command: hang\n> Here's your coat, Dev\n> Coats: 0\n> Good night","hidden":true},{"name":"Test 3","stdin":"","call":"CoatCheck check = new CoatCheck();\ncheck.leave(\"Eli\");\nUserInterface ui = new UserInterface(new Scanner(\"leave\\nFay\\ncollect\\n1\\nleave\\nGus\\ncoats\\nquit\\n\"), check);\nui.start();\nSystem.out.println(check.collect(1) + \" \" + check.collect(2));\n","expect":"> Fay, your ticket is 2\n> Here's your coat, Eli\n> Gus, your ticket is 1\n> Coats: 2\n> Good night\nGus Fay","hidden":true}],"require":[],"forbid":[],"more":[{"task":"A savings account program. Write the logic class, `Account.java`, and the user interface, `UserInterface.java`. `Main.java` is ready.\n\n`Account` has a balance, 0 at the start, and a history of the changes to it, in a list: a deposit adds a positive number to the history, a withdrawal a negative one. Its methods:\n\n- `deposit(int amount)`: adds the amount to the balance, and to the history.\n- `withdraw(int amount)`: if the balance is too small, changes nothing and returns `false`. Otherwise takes the amount off the balance, adds `-amount` to the history, and returns `true`.\n- `getBalance()`: returns the balance.\n- `history()`: returns the history, an `ArrayList<Integer>`.\n\n`UserInterface` takes a `Scanner` and an `Account`, and `start()` reads commands, with the prompt `> ` before each one:\n\n- `deposit`, then a line with the amount: print `Balance: ` and the new balance.\n- `withdraw`, then a line with the amount: print `Balance: ` and the new balance, or `Not enough money`.\n- `history`: print `History: ` and the list, like `History: [50, -20]`.\n- `quit`: print `Goodbye` and return.\n- Anything else: print `Unknown command: ` and the command.\n\nFor the input shown, the program prints:\n\n```\n> Balance: 50\n> Balance: 30\n> Not enough money\n> Balance: 45\n> History: [50, -20, 15]\n> Goodbye\n```\n","kind":"code","seed":"// ==== Account.java ====\nimport java.util.ArrayList;\n\npublic class Account {\n    // Write the logic here.\n}\n\n// ==== UserInterface.java ====\nimport java.util.Scanner;\n\npublic class UserInterface {\n    // Write the user interface here.\n}\n\n// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        Account account = new Account();\n        UserInterface ui = new UserInterface(scanner, account);\n        ui.start();\n    }\n}\n","solution":"// ==== Account.java ====\nimport java.util.ArrayList;\n\npublic class Account {\n    // Write the logic here.\n    private int balance;\n    private ArrayList<Integer> history;\n\n    public Account() {\n        this.balance = 0;\n        this.history = new ArrayList<>();\n    }\n\n    public void deposit(int amount) {\n        this.balance = this.balance + amount;\n        this.history.add(amount);\n    }\n\n    public boolean withdraw(int amount) {\n        if (amount > this.balance) {\n            return false;\n        }\n        this.balance = this.balance - amount;\n        this.history.add(-amount);\n        return true;\n    }\n\n    public int getBalance() {\n        return this.balance;\n    }\n\n    public ArrayList<Integer> history() {\n        return this.history;\n    }\n}\n\n// ==== UserInterface.java ====\nimport java.util.Scanner;\n\npublic class UserInterface {\n    // Write the user interface here.\n    private Scanner scanner;\n    private Account account;\n\n    public UserInterface(Scanner scanner, Account account) {\n        this.scanner = scanner;\n        this.account = account;\n    }\n\n    public void start() {\n        while (true) {\n            System.out.print(\"> \");\n            String command = this.scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye\");\n                break;\n            }\n            if (command.equals(\"deposit\")) {\n                int amount = Integer.valueOf(this.scanner.nextLine());\n                this.account.deposit(amount);\n                System.out.println(\"Balance: \" + this.account.getBalance());\n            } else if (command.equals(\"withdraw\")) {\n                int amount = Integer.valueOf(this.scanner.nextLine());\n                if (this.account.withdraw(amount)) {\n                    System.out.println(\"Balance: \" + this.account.getBalance());\n                } else {\n                    System.out.println(\"Not enough money\");\n                }\n            } else if (command.equals(\"history\")) {\n                System.out.println(\"History: \" + this.account.history());\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n\n// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        Account account = new Account();\n        UserInterface ui = new UserInterface(scanner, account);\n        ui.start();\n    }\n}\n","hints":["Write `Account` first: `private int balance;` and `private ArrayList<Integer> history;`, both set in the constructor. `withdraw` returns `false` first when `amount > this.balance`.","In `withdraw`, `this.history.add(-amount);` adds the amount as a negative number. The user interface prints the list itself: `\"History: \" + this.account.history()`."],"tests":[{"name":"Test 1","stdin":"deposit\n50\nwithdraw\n20\nwithdraw\n100\ndeposit\n15\nhistory\nquit\n","expect":"> Balance: 50\n> Balance: 30\n> Not enough money\n> Balance: 45\n> History: [50, -20, 15]\n> Goodbye","hidden":false},{"name":"Test 2","stdin":"history\nwithdraw\n1\ndeposit\n10\nwithdraw\n10\nbalance\nhistory\nquit\n","expect":"> History: []\n> Not enough money\n> Balance: 10\n> Balance: 0\n> Unknown command: balance\n> History: [10, -10]\n> Goodbye","hidden":true},{"name":"Test 3","stdin":"","call":"Account a = new Account();\nSystem.out.println(a.withdraw(1) + \" \" + a.getBalance() + \" \" + a.history());\na.deposit(100);\nSystem.out.println(a.withdraw(100) + \" \" + a.getBalance() + \" \" + a.history());\n","expect":"false 0 []\ntrue 0 [100, -100]","hidden":true},{"name":"Test 4","stdin":"","call":"Account a = new Account();\nUserInterface ui = new UserInterface(new Scanner(\"deposit\\n70\\nwithdraw\\n30\\nquit\\n\"), a);\nui.start();\nSystem.out.println(a.getBalance());\n","expect":"> Balance: 70\n> Balance: 40\n> Goodbye\n40","hidden":true}],"require":[],"forbid":[{"pattern":"\\bclass\\s+Account\\b[\\s\\S]*\\bSystem\\s*\\.\\s*out\\b[\\s\\S]*\\bclass\\s+UserInterface\\b","message":"Account doesn't print. Its methods return their answers, and the user interface prints them."}]},{"task":"A library card program, all of it: `LibraryCard.java`, `UserInterface.java` and `Main.java`.\n\n`LibraryCard` holds the titles of the books borrowed on the card, and a limit to how many books can be borrowed at a time. Its methods:\n\n- `LibraryCard(int limit)`: a card with no books, for at most `limit` books at a time.\n- `has(String title)`: returns `true` when that book is borrowed on the card.\n- `isFull()`: returns `true` when `limit` books are borrowed.\n- `borrow(String title)`: adds the book to the card. The user interface only calls it when the card isn't full and doesn't have the book.\n- `giveBack(String title)`: removes the book and returns `true`, or returns `false` if it isn't on the card.\n- `books()`: returns the titles, an `ArrayList<String>`, in the order they were borrowed.\n\n`UserInterface` takes a `Scanner` and a `LibraryCard`, and `start()` reads commands, with the prompt `> ` before each one:\n\n- `borrow`, then a line with a title: print `You already have ` and the title when the card has it, otherwise `Your card is full` when it's full, and otherwise borrow the book and print `Borrowed ` and the title.\n- `return`, then a line with a title: give the book back and print `Returned ` and the title, or print the title and ` isn't on your card`.\n- `books`: print `Books: ` and the list of titles.\n- `quit`: print `See you soon` and return.\n- Anything else: print `Unknown command: ` and the command.\n\n`main` creates a card with the limit 3 and starts the user interface. For the input shown, the program prints:\n\n```\n> Borrowed Emma\n> Borrowed Dune\n> You already have Emma\n> Borrowed Ivanhoe\n> Your card is full\n> Returned Dune\n> Dune isn't on your card\n> Borrowed Beloved\n> Books: [Emma, Ivanhoe, Beloved]\n> See you soon\n```\n","kind":"code","seed":"// ==== LibraryCard.java ====\nimport java.util.ArrayList;\n\npublic class LibraryCard {\n    // Write the logic here.\n}\n\n// ==== UserInterface.java ====\nimport java.util.Scanner;\n\npublic class UserInterface {\n    // Write the user interface here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        // Create the objects and start the user interface.\n    }\n}\n","solution":"// ==== LibraryCard.java ====\nimport java.util.ArrayList;\n\npublic class LibraryCard {\n    // Write the logic here.\n    private ArrayList<String> titles;\n    private int limit;\n\n    public LibraryCard(int limit) {\n        this.titles = new ArrayList<>();\n        this.limit = limit;\n    }\n\n    public boolean has(String title) {\n        return this.titles.contains(title);\n    }\n\n    public boolean isFull() {\n        return this.titles.size() >= this.limit;\n    }\n\n    public void borrow(String title) {\n        this.titles.add(title);\n    }\n\n    public boolean giveBack(String title) {\n        if (!this.titles.contains(title)) {\n            return false;\n        }\n        this.titles.remove(title);\n        return true;\n    }\n\n    public ArrayList<String> books() {\n        return this.titles;\n    }\n}\n\n// ==== UserInterface.java ====\nimport java.util.Scanner;\n\npublic class UserInterface {\n    // Write the user interface here.\n    private Scanner scanner;\n    private LibraryCard card;\n\n    public UserInterface(Scanner scanner, LibraryCard card) {\n        this.scanner = scanner;\n        this.card = card;\n    }\n\n    public void start() {\n        while (true) {\n            System.out.print(\"> \");\n            String command = this.scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"See you soon\");\n                break;\n            }\n            if (command.equals(\"borrow\")) {\n                String title = this.scanner.nextLine();\n                if (this.card.has(title)) {\n                    System.out.println(\"You already have \" + title);\n                } else if (this.card.isFull()) {\n                    System.out.println(\"Your card is full\");\n                } else {\n                    this.card.borrow(title);\n                    System.out.println(\"Borrowed \" + title);\n                }\n            } else if (command.equals(\"return\")) {\n                String title = this.scanner.nextLine();\n                if (this.card.giveBack(title)) {\n                    System.out.println(\"Returned \" + title);\n                } else {\n                    System.out.println(title + \" isn't on your card\");\n                }\n            } else if (command.equals(\"books\")) {\n                System.out.println(\"Books: \" + this.card.books());\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n\n// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        // Create the objects and start the user interface.\n        Scanner scanner = new Scanner(System.in);\n        LibraryCard card = new LibraryCard(3);\n        UserInterface ui = new UserInterface(scanner, card);\n        ui.start();\n    }\n}\n","hints":["Follow the plan: `LibraryCard` first, with a list of titles and the limit as instance variables. `has` is the list's `contains`, `isFull` compares the list's `size()` with the limit, and `giveBack` checks `contains` before removing.","The `borrow` command is an `if`, `else if`, `else`: first `this.card.has(title)`, then `this.card.isFull()`, and only then `this.card.borrow(title)`. `main` is the same four lines as in the other programs with a UserInterface class, with `new LibraryCard(3)`."],"tests":[{"name":"Test 1","stdin":"borrow\nEmma\nborrow\nDune\nborrow\nEmma\nborrow\nIvanhoe\nborrow\nBeloved\nreturn\nDune\nreturn\nDune\nborrow\nBeloved\nbooks\nquit\n","expect":"> Borrowed Emma\n> Borrowed Dune\n> You already have Emma\n> Borrowed Ivanhoe\n> Your card is full\n> Returned Dune\n> Dune isn't on your card\n> Borrowed Beloved\n> Books: [Emma, Ivanhoe, Beloved]\n> See you soon","hidden":false},{"name":"Test 2","stdin":"books\nreturn\nEmma\nborrow\nPersuasion\nrenew\nbooks\nreturn\nPersuasion\nbooks\nquit\n","expect":"> Books: []\n> Emma isn't on your card\n> Borrowed Persuasion\n> Unknown command: renew\n> Books: [Persuasion]\n> Returned Persuasion\n> Books: []\n> See you soon","hidden":true},{"name":"Test 3","stdin":"","call":"LibraryCard card = new LibraryCard(2);\ncard.borrow(\"A\");\nSystem.out.println(card.has(\"A\") + \" \" + card.has(\"B\") + \" \" + card.isFull());\ncard.borrow(\"B\");\nSystem.out.println(card.isFull() + \" \" + card.books());\nSystem.out.println(card.giveBack(\"A\") + \" \" + card.giveBack(\"A\") + \" \" + card.books());\n","expect":"true false false\ntrue [A, B]\ntrue false [B]","hidden":true},{"name":"Test 4","stdin":"","call":"LibraryCard card = new LibraryCard(1);\nUserInterface ui = new UserInterface(new Scanner(\"borrow\\nOdyssey\\nborrow\\nIliad\\nborrow\\nOdyssey\\nbooks\\nquit\\n\"), card);\nui.start();\nSystem.out.println(card.books());\n","expect":"> Borrowed Odyssey\n> Your card is full\n> You already have Odyssey\n> Books: [Odyssey]\n> See you soon\n[Odyssey]","hidden":true}],"require":[],"forbid":[{"pattern":"\\bclass\\s+LibraryCard\\b[\\s\\S]*\\bSystem\\s*\\.\\s*out\\b[\\s\\S]*\\bclass\\s+UserInterface\\b","message":"LibraryCard doesn't print. Its methods return their answers, and the user interface prints them."}]}]}