Now that clients can create PhoneEntry
objects, let us consider how to allow clients to destroy them again. One obvious design is to add a destroy
operation to the factory — after all, seeing that a factory knows how to create objects, it stands to reason that it also knows how to destroy them again:
exception PhoneEntryNotExists { string name; string phNum; }; interface PhoneEntryFactory { PhoneEntry* create(string name, string phNum) throws PhoneEntryExists; void destroy(PhoneEntry* pe) // Bad idea! throws PhoneEntryNotExists; };
While this works (and certainly can be implemented without problems), it is generally a bad idea. For one, an immediate problem we need to deal with is what should happen if a client passes a proxy to an already-destroyed object to destroy
. We could raise an ObjectNotExistException
to indicate this, but that is not a good idea because it makes it ambiguous as to which object does not exist: the factory, or the entry. (By convention, if a client receives an ObjectNotExistException
for an invocation, what does not exist is the object the operation was targeted at, not some other object that in turn might be contacted by the operation.) This forces us to add a separate PhoneEntryNotExists
exception to deal with the error condition, which makes the interface a little more complex.
A second and more serious problem with this design is that, in order to destroy an entry, the client must not only know which entry to destroy, but must also know which factory created the entry. For our example, with only a single factory, this is not a real concern. However, for more complex systems with dozens of factories (possibly in multiple server processes), it rapidly becomes a problem: for each object, the application code somehow has to keep track of which factory created what object; if any part of the code ever loses track of where an object originally came from, it can no longer destroy that object.
Of course, we could mitigate the problem by adding an operation to the PhoneEntry
interface that returns a proxy to its factory. That way, clients could ask each object to provide the factory that created the object. However, that needlessly complicates the Slice definitions and really is just a band-aid on a fundamentally flawed design. A much better choice is to add the destroy
operation to the PhoneEntry
interface instead:
interface PhoneEntry { idempotent string name(); idempotent string getNumber(); idempotent void setNumber(string phNum); void destroy(); };
With this approach, there is no need for clients to somehow keep track of which factory created what object. Instead, given a proxy to a PhoneEntry
object, a client simply invokes the destroy
operation on the object and the PhoneEntry
obligingly commits suicide. Note that we also no longer need a separate exception to indicate the "object does not exist" condition because we can raise ObjectNotExistException
instead — the exception exists precisely to indicate this condition and, because destroy
is now an operation on the phone entry itself, there is no ambiguity about which object it is that does not exist.
Topics
- Idempotency and Life Cycle Operations
- Implementing a destroy Operation
- Cleaning Up a Destroyed Servant
- Life Cycle and Collection Operations
- Life Cycle and Normal Operations