Java Arena
Step 3 of 7

The program's logic in a class

The practice log's main does two jobs at once. It talks with the user: it prints the prompt, reads commands and prints answers. And it does the work: it keeps the sessions, adds them up and finds the longest. The two are woven together, and each new command makes the knot bigger. The first step toward untangling them is to move the work into a class of its own:

Main.java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        PracticeLog log = new PracticeLog();
        while (true) {
            System.out.print("> ");
            String command = scanner.nextLine();
            if (command.equals("quit")) {
                System.out.println("Keep practicing!");
                break;
            }
            if (command.equals("log")) {
                int minutes = Integer.valueOf(scanner.nextLine());
                log.add(minutes);
                System.out.println("Logged " + minutes + " minutes");
            } else if (command.equals("total")) {
                System.out.println("Total: " + log.total() + " minutes");
            } else if (command.equals("longest")) {
                System.out.println("Longest: " + log.longest() + " minutes");
            } else {
                System.out.println("Unknown command: " + command);
            }
        }
    }
}
PracticeLog.java
import java.util.ArrayList;

public class PracticeLog {
    private ArrayList<Integer> sessions;

    public PracticeLog() {
        this.sessions = new ArrayList<>();
    }

    public void add(int minutes) {
        this.sessions.add(minutes);
    }

    public int total() {
        int total = 0;
        for (int minutes : this.sessions) {
            total = total + minutes;
        }
        return total;
    }

    public int longest() {
        int longest = 0;
        for (int minutes : this.sessions) {
            if (minutes > longest) {
                longest = minutes;
            }
        }
        return longest;
    }
}
Input
log
25
log
40
total
log
15
longest
quit
Output
> Logged 25 minutes
> Logged 40 minutes
> Total: 65 minutes
> Logged 15 minutes
> Longest: 40 minutes
> Keep practicing!

The program does exactly what it did before, but the jobs are now apart:

  • PracticeLog is the program's logic: what the program knows and works out. It keeps the sessions and answers questions about them with return values. It never reads input and never prints anything.
  • main is the user interface, or UI: the part that talks with the user. It reads the commands, calls the log's methods, and turns their answers into sentences.
  • The loop in main now reads like a list of the commands. How the total is added up is no longer its business: log.total() says what it needs.

Why the split helps

  • Each part can be read and changed on its own. To change what a message says, you look at the UI. To change how the longest session is found, you look at PracticeLog, and the UI doesn't care.
  • The logic can be used without anyone typing anything: PracticeLog log = new PracticeLog(); log.add(30); System.out.println(log.total());. That's how the checks on this site test your classes, and module 26 builds on it.
  • The same class could serve another kind of user interface, such as an app with buttons, without a change. That's why it returns numbers and doesn't print sentences: how an answer is shown is for the UI to decide.

A quick test for a logic class: could a program without a keyboard or a screen use it? If the class has a Scanner or a System.out.println in it, it couldn't.

Your task

Challenge 1 of 3

Here's the campsite of the previous step, split in two. Main.java is the user interface, and it's ready. Write the logic class in Campsite.java:

  • Campsite(int spots): an empty campsite with that many tent spots.
  • checkIn(): if a spot is free, one more spot is taken and the method returns true. If every spot is taken, nothing changes and it returns false.
  • checkOut(): if a spot is taken, it's free again and the method returns true. If no spot is taken, nothing changes and it returns false.
  • freeSpots(): returns the number of free spots.

The class doesn't print anything: main does. For the input shown, the program prints:

> Welcome! Free spots: 2
> Welcome! Free spots: 1
> Free spots: 1
> Welcome! Free spots: 0
> Sorry, the campsite is full
> Safe travels! Free spots: 1
> Lights out

Challenge 2

A thermostat's logic is ready in Thermostat.java. warmer() and cooler() change the target temperature by one degree and return true. If the target is already at its limit, 28 or 15 degrees, they change nothing and return false. getTarget() returns the target.

Write the user interface in Main.java: create a Thermostat with the target 21, and read commands, with the prompt > before each one:

  • up: make it one degree warmer and print the new target like Target: 22 degrees, or Already at the maximum when it can't go higher.
  • down: make it one degree cooler and print the new target, or Already at the minimum.
  • show: print the target like Target: 21 degrees.
  • off: print Thermostat off and end the program.
  • Anything else: print Unknown command: and the command.

For the input shown, the program prints:

> Target: 21 degrees
> Target: 22 degrees
> Target: 23 degrees
> Target: 22 degrees
> Unknown command: heat
> Thermostat off

Challenge 3

Here's the deli counter of the previous step, with everything in main. It works, but the list and the work on it are tangled with the talking. Move the logic into DeliLine.java, so that main keeps only the prompt, the reading and the printing. The class needs:

  • DeliLine(): an empty line.
  • take(String name): adds the name to the end of the line and returns their place in it, 1 for the first.
  • isEmpty(): returns true when nobody is waiting.
  • next(): takes the first name off the line and returns it. main only calls it when someone is waiting.
  • toString(): returns the names in line, like [Ben, Cleo].

Then change main to use a DeliLine instead of the list. The program prints exactly what it did. For the input shown:

> Ana, you're number 1
> Ben, you're number 2
> Now serving Ana
> Cleo, you're number 2
> In line: [Ben, Cleo]
> Now serving Ben
> Now serving Cleo
> Nobody is waiting
> Closed for today