Java Arena
Step 4 of 6

Packages by responsibility

Module 25 split a program into a user interface and its logic, each in a class of its own. Packages make that split visible in the folders. A common layout has three packages, each with one responsibility, inside a package named after the program:

  • quiz.domain: the classes of the things the program is about, such as a question. They hold their data and the rules about themselves, such as whether a reply is right.
  • quiz.logic: what the program does with them. Here, a class Quiz keeps the questions, checks the answers and counts the score. The logic doesn't read input and doesn't print.
  • quiz.ui: the user interface. TextUI reads what the user types, asks the logic to do the work, and prints the results.

Main stays at the top and puts the pieces together:

Main.java
import java.util.Scanner;
import quiz.domain.Question;
import quiz.logic.Quiz;
import quiz.ui.TextUI;

public class Main {
    public static void main(String[] args) {
        Quiz quiz = new Quiz();
        quiz.add(new Question("Which planet is the largest?", "Jupiter", 2));
        quiz.add(new Question("What is the chemical symbol for gold?", "Au", 3));
        quiz.add(new Question("How many legs does a spider have?", "8", 2));
        TextUI ui = new TextUI(new Scanner(System.in), quiz);
        ui.start();
    }
}
quiz/domain/Question.java
package quiz.domain;

public class Question {
    private String prompt;
    private String answer;
    private int points;

    public Question(String prompt, String answer, int points) {
        this.prompt = prompt;
        this.answer = answer;
        this.points = points;
    }

    public boolean isRight(String reply) {
        return reply.trim().equalsIgnoreCase(this.answer);
    }

    public int getPoints() {
        return this.points;
    }

    @Override
    public String toString() {
        return this.prompt + " (" + this.points + " points)";
    }
}
quiz/logic/Quiz.java
package quiz.logic;

import java.util.ArrayList;
import quiz.domain.Question;

public class Quiz {
    private ArrayList<Question> questions;
    private int score;

    public Quiz() {
        this.questions = new ArrayList<>();
        this.score = 0;
    }

    public void add(Question question) {
        this.questions.add(question);
    }

    public ArrayList<Question> getQuestions() {
        return this.questions;
    }

    public boolean answer(Question question, String reply) {
        if (question.isRight(reply)) {
            this.score += question.getPoints();
            return true;
        }
        return false;
    }

    public int getScore() {
        return this.score;
    }
}
quiz/ui/TextUI.java
package quiz.ui;

import java.util.Scanner;
import quiz.domain.Question;
import quiz.logic.Quiz;

public class TextUI {
    private Scanner scanner;
    private Quiz quiz;

    public TextUI(Scanner scanner, Quiz quiz) {
        this.scanner = scanner;
        this.quiz = quiz;
    }

    public void start() {
        for (Question question : this.quiz.getQuestions()) {
            System.out.println(question);
            System.out.print("> ");
            String reply = this.scanner.nextLine();
            if (this.quiz.answer(question, reply)) {
                System.out.println("Right!");
            } else {
                System.out.println("Not quite.");
            }
        }
        System.out.println("Your score: " + this.quiz.getScore());
    }
}
Input
jupiter
Ag
8
Output
Which planet is the largest? (2 points)
> Right!
What is the chemical symbol for gold? (3 points)
> Not quite.
How many legs does a spider have? (2 points)
> Right!
Your score: 4

Quiz never prints: answer returns true or false, and TextUI decides what to show. TextUI never counts points: it asks Quiz. And Question knows nothing of either of them.

Which may use which

The three packages use each other in one direction only:

  • ui uses logic, and domain too: TextUI shows the questions.
  • logic uses domain.
  • domain uses neither of the others.

The imports at the top of each file show this at a glance. TextUI imports from quiz.logic and quiz.domain, Quiz from quiz.domain, and Question from nothing of the program. An import of quiz.ui in a file of quiz.logic or quiz.domain would mean that the direction is wrong.

Why one direction?

  • The logic and the domain work with any user interface. The same Quiz could get a graphical one, like the programs with windows later in this course, or be run by tests, without a change to its code.
  • A change in how the program looks touches only ui, and a change in its rules touches only logic or domain.
  • Each package is small enough to read on its own. To see how the score is counted, open logic.

Where does a change go?

The change The package, and where in it
A question gets a category, such as Science domain: an instance variable and a getter in Question
A wrong answer costs one point logic: the answer method of Quiz
The score is shown after every question ui: the loop in TextUI's start

Your task

Challenge 1 of 3

A food truck's program has the package foodtruck.domain, with the class Dish, ready: a dish has a name and a price in whole dollars, with getName(), getPrice() and a toString() like Taco (4 dollars). In foodtruck/logic/Order.java, write the logic class Order, in the package foodtruck.logic:

  • a constructor Order(), which makes an empty order
  • add(Dish dish), which adds a dish to the order (the same dish can be ordered several times)
  • count(), which returns the number of dishes in the order
  • total(), which returns the total price of the order
  • mostExpensive(), which returns the most expensive dish of the order (the first one ordered, if several cost the most), or null for an empty order

Order is logic: it returns values and prints nothing. The check runs:

Order order = new Order();
order.add(new Dish("Taco", 4));
order.add(new Dish("Burrito", 9));
order.add(new Dish("Taco", 4));
System.out.println(order.count() + " dishes, " + order.total() + " dollars");
System.out.println("Most expensive: " + order.mostExpensive());

and expects:

3 dishes, 17 dollars
Most expensive: Burrito (9 dollars)

Challenge 2

A cattery's program works, but its logic prints: checkIn and checkOut in Cattery print their own messages. No other user interface could use them, and nothing can ask them whether they worked.

Change the two methods in Cattery.java so that they print nothing and return a boolean: checkIn returns whether the cat got a pen, and checkOut whether a cat of that name was there to go home. Then make TextUI.java print the messages, which stay exactly as they are. For the input shown, the program prints:

> Mochi checked in
> Bella checked in
> No free pen for Max
> Cats: 2
> No cat called Max
> Mochi went home
> Cats: 1
> Bye!

Challenge 3

The food truck gets its user interface. Dish.java and Order.java are the ones from the first task, and Menu.java, in foodtruck.logic, is ready: add(Dish dish) puts a dish on the menu, and find(String name) returns the dish of that name, with capital letters or not, or null if the menu has none. Main.java is ready too.

In foodtruck/ui/TextUI.java, write the class TextUI, in the package foodtruck.ui, with the imports it needs. Its constructor takes a Scanner, a Menu and an Order, and its method start() reads commands, with the prompt > before each one:

  • order: the next line is the name of a dish. If the menu has it, add it to the order and print Added and the dish, like Added Taco (4 dollars). Otherwise print Not on the menu: and the name.
  • total: print the number of dishes and the total, like 3 dishes, 17 dollars.
  • done: print Thank you! You pay and the total and dollars, and return.
  • Anything else: print Unknown command: and the command.

For the input shown, the program prints:

> Added Taco (4 dollars)
> Added Burrito (9 dollars)
> Not on the menu: Pizza
> Added Taco (4 dollars)
> 3 dishes, 17 dollars
> Thank you! You pay 17 dollars