Object-oriented boundary exercises check whether every public operation preserves the object’s stated invariant under rejected as well as accepted inputs.
Java OOP exercises: invariant-preserving reservations and independent snapshots
Java 8+. This is a complete program using JDK classes.
Implement before reading the solution
Create a stock reservation object with non-negative available and reserved counts. reserve rejects non-positive quantities and returns false when stock is insufficient. A rejected request must not change either field. Expose an independent two-number snapshot rather than the object’s internal storage.
Test a successful reservation, an over-capacity request, zero input and mutation of a returned snapshot. State whether invalid input throws or returns a rejected result; this solution throws for a malformed quantity and returns false for a valid request that exceeds available stock.
The two rejection reasons are intentionally different. A UI can ask the customer to choose a smaller amount after insufficient stock, but zero is not a valid reservation quantity. A caller should not need to inspect a partially updated object to determine which branch occurred.
Working program
public class ReservationBoundaryExercises {
static final class Stock {
private int available,reserved;
Stock(int available){if(available<0)throw new IllegalArgumentException("Negative stock");this.available=available;}
boolean reserve(int quantity){
if(quantity<=0)throw new IllegalArgumentException("Positive quantity required");
if(quantity>available)return false;
available-=quantity;reserved+=quantity;return true;
}
int[] snapshot(){return new int[]{available,reserved};}
}
static void require(boolean condition){if(!condition)throw new AssertionError("Boundary failed");}
public static void main(String[] args){
Stock stock=new Stock(10);require(stock.reserve(3));
require(!stock.reserve(20));require(stock.snapshot()[0]==7&&stock.snapshot()[1]==3);
try{stock.reserve(0);throw new AssertionError("Zero accepted");}catch(IllegalArgumentException expected){ }
int[] detached=stock.snapshot();detached[0]=99;require(stock.snapshot()[0]==7);
System.out.println("4 reservation checks passed");
}
}Output
4 reservation checks passedCosts and boundaries
Each operation touches fixed-sized state and uses O(1) work. Each snapshot allocates a two-element array. This model is single-threaded; sharing it between workers requires protecting the full available/reserved transition, not just marking each field volatile.
Common Mistakes
- Reject before mutating.
- A snapshot must not expose owned mutable storage.
- Separate malformed requests from valid requests that cannot be fulfilled.
