ATM Diagrams
class Item {
protected final int id;
private static int numberOfItemsCreated = 0;
public Item() {id = numberOfItemsCreated++;}
// pretend that there are more members here...
}
Before moving on, let's clean up one thing. We should have written:
class Item {
protected final int id = numberOfItemsCreated++;
private static int numberOfItemsCreated = 0;
}
The reason is that if we needed to overload constructors within Item, we'd have to remember to put the increment logic in each (or make all the overloaded versions call one that did. That's not impossible; it's just one more thing we might get wrong. (Also, I realized I left out the final modifier on the assignment sheet -- it was copy/pasted/edited from a C++ version of the question!)
Okay, now back to the problem. How can we decouple the idea of unique id generation from the classes that need it?
A completely "automatic" solution is near impossible anyway. Let's call an automatic solution one that, just by marking a class in a special way, gives all instances of that class a field called id with a unique final value. We can't really achieve this anyway, since if you already have a class C extending another class S, but S already has a field called id, how are you going to get a new id field?
Not only can we not get a completely automatic solution, but we also have a couple of nagging problems here: our example is not thread-safe, and we are using integer ids which we can easily run out of. We could use long integers instead, or BigIntegers. If we use BigIntegers, which are objects, we have to be careful not to induce sharing!
Here's the best solution we can get, and to no one's surprise, it is based on composition and not inheritance! The most automatic solution we can get would look like this:
class Item {
protected final BigInteger id = Generator.next();
// ...
}
Cool, just one line. All we have to do now is make the generator class. Note that by making the Generator class external to the Item class, we can encapsulate all the synchronization logic and BigInteger manipulation. It's kind of tricky to get right; you have to resist the tendency to return a reference to the generator's internal value in your next() method:
class Generator {
private static BigInteger value = new BigInteger("0");
public static synchronized BigInteger next() {
value = value.add(BigInteger.ONE);
return value.add(BigInteger.ZERO); // not just "return value"!!
}
}
This solution will give unique ids across all classes that use the Generator class. If you need unique ids you only have to add one line to a class.
An interesting exercise is to modify the solution so that ids are unique only within a class but not necessarily system-wide unique.