Close the planner solution. This independent brief uses a different domain so you can check whether the ideas transfer. Spend time writing a contract and acceptance table before opening hints. It is fine to revisit a particular earlier lesson, but try not to copy and rename the final project wholesale.
The brief
Model a Book with a trimmed nonblank title, a fixed total page count and a changing number of pages read. The constructor accepts title and pages; pages must be nonnegative. Start with zero read. Throw IllegalArgumentException for a null or blank title or a negative page count. A zero-page book is allowed and has zero remaining pages immediately.
Provide readPages(int amount) returning boolean. Accept a nonnegative amount only when it fits in the unread remainder; otherwise return false without changing state. Provide summary() returning title followed by the remaining-page count. Repeated reads are genuine additional work, unlike the planner’s complete action. Decide which state changes and explain this difference.
Acceptance plan
For a 100-page Java book, reading 25 succeeds and leaves 75. Reading -1 or 80 next fails and still leaves 75. Reading exactly 75 succeeds and leaves 0. A read of 0 succeeds without changing state. A second book must remain independent. Check constructor rejection separately from rejected updates.
For a multi-file version, use Main.java for your checks and Book.java for the model, with the class matching its filename. The two-tab workspace below supplies those filenames and deliberately incomplete methods. Implement the model in Book.java and add checks in Main.java. You can also download this starter for local work; all core practice still works in the browser.
public class Main {
public static void main(String[] args) {
int capacity = 100;
int used = 25;
int request = 80;
boolean fits = request >= 0 && request <= capacity - used;
System.out.println(fits);
System.out.println(capacity - used);
}
}Check your prediction
false
75 The fit test uses the remaining capacity before mutation. It does not add request to used first. Testing against capacity-used also avoids overflow from adding two potentially large positive ints.
Your turn
Build and check Book
Implement the brief and print the following for a 100-page Java book: successful read of 25 (true), rejected read of 80 (false), summary Java: 75 pages left. Add the remaining acceptance checks yourself after this first case passes.
Hint 1: the idea
Keep fixed values final and the progress value mutable. Validate a request before adding it.
Hint 2: a first step
Use pages-read as the available remainder. If a request is outside its allowed range, return false before assigning to read.
Show one solution
This reference is shown as one Main.java file so you can read it together. In
the two-tab workspace, put the Book fields and method bodies in Book.java,
keeping its public class Book declaration. Put only the Main code in Main.java.
Do not paste both class declarations into both tabs.
public class Main {
public static void main(String[] args) {
Book book = new Book(" Java ", 100);
System.out.println(book.readPages(25));
System.out.println(book.readPages(80));
System.out.println(book.summary());
}
}
class Book {
private final String title;
private final int pages;
private int read;
Book(String title, int pages) {
if (title == null || title.trim().isEmpty()) {
throw new IllegalArgumentException("Title is required");
}
if (pages < 0) {
throw new IllegalArgumentException("Pages must be nonnegative");
}
this.title = title.trim();
this.pages = pages;
}
public boolean readPages(int amount) {
if (amount < 0 || amount > pages - read) {
return false;
}
read += amount;
return true;
}
public String summary() {
return title + ": " + (pages - read) + " pages left";
}
}true
false
Java: 75 pages left Change it and explain
Turn the acceptance plan into a test program with labeled checks. For a multi-file version, verify the same Book from a separate Main entry. Add one deliberate mutation—accept requests above the remainder—and make sure a check detects it.
Explain why a setter such as setRead(anyNumber) is not part of this model’s API. The public operation expresses progress and preserves a bound. If you needed corrections or undo later, design that behavior explicitly rather than quietly allowing every caller to bypass the current rule.
Completion is saved in this browser only. It does not measure mastery.