I don't want to get into the can-tests-really-prevent-bugs debate. My intention here is just to show what I mean by a bulletproof design. Here is a simple (non-bulletproof) Flight class:
public class Flight {
private final int numSeats;
private final Set<Traveler> travelers
= new HashSet<Traveler>();
public Flight(int numSeats_) {
numSeats = numSeats_;
}
public void book(Traveler t) {
if(travelers.size() >= numSeats)
throw new RuntimeException("Fully booked");
travelers.add(t);
}
public void unbook(Traveler t) {
if(!travelers.contains(t))
throw new IllegalArgumentException("t=" + t);
travelers.remove(t);
}
}
public class Traveler {
...
}
The predicament lies in the Flight.unbook() method. If we pass the wrong argument, this method will fail:
Filght f1 = ...;
Filght f2 = ...;
Traveler t = ...;
f1.book(t);
f2.unbook(t); // Illegal argument error!
And now, let's see the bulletproof approach. The trick is to use static type checking - sometimes in conjunction with encapsulation - to make it impossible to issue an incorrect unbook() request:
public class Flight {
private final int numSeats;
private final Set<Traveler> travelers
= new HashSet<Traveler>();
public Flight(int numSeats_) {
numSeats = numSeats_;
}
public Traveler book(Traveler t) {
if(travelers.size() >= numSeats)
throw new RuntimeException("Fully booked");
travelers.add(t);
return new Reservation(t);
}
public class Reservation {
public final Traveler traveler;
public Reservation(Traveler t) {
traveler = t;
}
public void unbook() {
travelers.remove(traveler);
}
}
}
With this design unbook requests (now issued through a Reservation object) cannot fail:
Filght f1 = ...;
Filght f2 = ...;
Traveler t = ...;
Flight.Reservation r = f1.book(t);
r.unbook(); // Always safe!
0 comments :: Bulletproof
Post a Comment