Stateful
From APIDesign
A stateful object is an instance of a class that may morph itself into various states. For example an object can be created, but not initialized, later initialized and ready for being used and at the end it can be disposed (but still remain accessible in memory).
In contrast to this a stateless object represents just one state for its whole life-cycle. Modeling this as an immutable object, whose fields are only initialized once during construction and then remain unchanged, seems to be the most straightforward design technique.
Contents |
Unforgiving Stateful Nightmare
There is one common problem that everyone has to face when using APIs based on stateful objects. Often, depending on the object state only some methods of its public interface are accessible (e.g. can be called without throwing an exception). However not only these accessible methods are visible in Javadoc. All object's methods are visible in the public signature of its class and (without reading carefully its javadoc) it is not easy (and definitely not clueless) to find out which of the methods can be called immediately. As a result one may easily call inappropriate method designed for different phase.
Imagine a stateful API for displaying a progress:
does not exists: progress.api
and consider following usage. Is it correct or not?
does not exists: progress.wrong.order
No, it is not. This yields a runtime exception. The usage forgets to start the progress by calling its start method, which (according to documentation) has to be called sooner before notifying any progress.
The right question to ask is why such restriction is in place? Why cannot the API be more forgiving and automatically perform start when its first progress method is called? Is this an attempt of the API writer to punish its API readers for their cluelessness? Obviously blaming customers is not the right way to win their hearts. They need help to deal with states!
Call State Machine
During my last visit to Linz University I was presented to a research project to create an annotation based state machine javadoc enhancement. The plan is simple. Why not annotate the methods with:
does not exists: progress.annotations
Then the compiler could check whether the order of calls for each object is correct or not. The project team was describing this with great enthusiasm (deserved, tracking object state throughout program flow is tricky), but I was immediately thinking: This is not beautiful (in the sense of not being rationalistic solution)! Rather than writing poor stateful API and then trying to fix it with additional annotations, one shall write good API to begin with!
Converting to stateless API
If we split the methods into phase groups, then we can ensure proper transition between individual phases:
does not exists: progress.phases
Such API makes use of uninitialized objects impossible. Either one is in configuration phase, and then there is no progress method, or one enters the progress phase and then it is obviously clear that the object has already been configured.
This is an API that naturally guides users through the whole life cycle of an object (to be fair, I have to mention that it solves only the initialization path, there is no easy way to make an already existing object obsolete). In each phase only appropriate methods are available. Only such methods are shown in code completion inside an IDE. The possibility of making an error is minimized.
A little bit of forgiveness
I am going to claim that stateful API is a software antipattern (those who fight against stateful singletons can easily agree). Splitting such phased API into individual interfaces for each phase shall be good, rationalistic way of eliminating all associated problems.
However software engineering is not art, one cannot always start from scratch, with clean design. Sometimes one needs to maintain already created stateful API. In such situation any effort resembling the state machine checks can become more than welcomed. Such check is still a dirty, ugly, post-fix (made in the line of empiricism), but at least it helps to eliminate the most obvious misuse of such stateful API.
If none of the above is available, then please at least follow credo of MartinRinard's presentation Image:RinardOOPSLA06.pdf - don't throw needless exceptions unless you have to!. Of course, during development strive for proper order of calls (e.g. start must be before progress). But in production environment do your best to survive even wrong call order (which is easy in this case, just call the start automatically, if it has not been called yet).
Help your users to be clueless. They will reward you by not hating your API! Or at least their feelings are not going to be as negative as mine, whenever I have to deal with this one.
<comments/>