In the OO world you are frowned upon if you call something object. Its time to extend this principle: don’t call your state state. This post is about why this is a bad idea and what you can do about it.
Recently we sat down to discuss the new data model for our messaging system at eBay’s Classifieds Group. One of the things we inherited from the past is the entity called conversation with a field called state. Possible values were Ok, On hold and Blocked.
So what was the problem?
A field called ‘state’ almost always has a very intuitive meaning. Unfortunately, the word is so vague that the meaning can easily warp, depending on the problem at hand. I noticed this in a couple of projects: the state field started to collect more and more possible values. With more values came increasingly difficult state transitions. This lead to code that was way more messy then necessary.
For example, in our conversation entity we could introduce the state Closed to indicate that a participant wants to stop the conversation. Then we continue by adding the state Archived to indicate that the conversation should be hidden until a new messages arrive.
What can we do?
The key observation is that each state value represents multiple behaviors. Think about it, what behavior is needed in each state? How do these behaviors change for each state? These questions will lead you to multiple fields that can represent the entire state of your entities.
Within a couple of minutes we found three behaviors we wanted to have for our conversations: a conversation is either visible or not (field visibility with values Displayed and Hidden), it will accept new messages or not (field acceptNew with values Accept and Reject) and we want to notify the recipient of a new message (or not) (field notifyOnNew with values Notify and Mute).Not only did our code become easier to extend and reason about, as a bonus we found a feature that would have been really hard with the old model: muting a conversation.
Conclusion
Don’t call your state ‘state’, instead, think about the behavior each state represents and model that instead.
The problem with having multiple variables instead of a state variable, means that you can't do state transitions with a state table. States should only be used when they are mutually exclusive (ie a game character may be running, fighting, dying) don't use the state for something that can be independent. Say you have a firing state, then that may be better as a flag, which can be set even if he is running or fighting.
ReplyDeleteThe point of the article is:
ReplyDelete* you can have multiple states in your data model
* you should name them properly (literally, just don't call it 'state').
In your example call them for example movingBy, health and firingWith.
You can still group several states together to make state tables possible. They can also help with preventing invalid combinations (e.g. sleeping and firing).
'Mutual exclusion' is indeed required, but it is not a good tool to help you model your data. The states of our initial conversation model were also mutually exclusive.