Take a look at these methods:
refactoring.setState(IN_PROGRESS); refactoring.setState(VERIFIED); refactoring.setState(DONE);
To make everything clear, we can easily assume that each invocation is done in a different place and after different activities have been completed.
There is nothing exciting in this code and probably you saw something similar in your code bases many times. Nothing complicated. We are just changing the state of the object.
So, now’s the time for the question: what’s wrong with this code?
Implementation? Why?Before I give the answer to this question, let me list everything we know about the object by reading these three lines above:
- An object is stateful.
- There is setter so we are almost certain the method sets a value of the state attribute.
- We know what’s the type of a value of the state attribute. It’s some known enum.
We know what we know and now’s the time to find out what we don’t, but we would like to know.
When you are reading the list above you may notice that all this information is implementation-specific. We haven’t got a single piece of information about an object’s behavior.
Don’t you think it is a problem?
No behavior? At all?Functionality built on top of knowledge about an object’s implementation is extremely tightly coupled with this object. Why? Because we know about it not only what we need to know, but also the things that should be irrelevant in the place where we are using the object. To be more accurate, the object gives us only an access to the data based on which we may implement functionality that we need. In such case the object becomes nothing more but a state holder. Entire functionality which refers to this state is implement around the object. Around the details of the way how this object is built.
Maybe some of you at this moment recognize an anti-pattern that is known as anemic domain model?
Another problem is that implementation of our object is not encapsulated. It becomes part of the contract and any change of it may have a huge impact on many parts of our application. It may change functionality that is built on top of the implementation details.
I believe that it is not what you were dreaming about when you reading about the object-oriented programming for the first time.
The behavior we wantWhat can we do? We can put some life into our object. We can hide its guts from the outer world. We can do something like that:
refactoring.started(); refactoring.accepted(); refactoring.applied();
I believe that some of you at this moment thought “wait, but know I will have three methods that under the neath will do exactly the same?”
The answer is yes, all of those method will set the value of the state attribute. But does it really matter? Are we interested in the attribute value? Are we interested in the attribute at all? No, we just want to be sure that refactoring was started/accepted/applied. We don’t care about the way how it was implemented at all.
Thanks to such design we may changing implementation in any way we want as long as the contract conditions are met. Information about implementation are unknown at this moment. We know what we have to know and nothing except this. The functionality that is using the object is not so tightly coupled with its implementation details anymore.