In this article I want to show, why it is a problem to have two (or more) reasons for a change or for the creation of something.
The event that made me think about this topic was an email of a friend of mine discussing some design issues in our exceptions library. He pointed out that there are two reasons for creating an exception class for Java:
- You want to catch an exception and handle it individually.
- You require specific information to be passed to the exception to document the context in which the exception has been raised.
I want to explain the problem with having two reasons in the light of
- Reducing Complexity
- Revealing Intention
- Reducing Conflict
Robert C. Martin
So reducing complexity by having only one responsibility is one of the reasons to have only one reason for a change.
In his book Practical API Design Jaroslav Tulach encourages to create APIs whose elements have a clear intention. One way to express expected API use is through modifiers of methods.
|Access Modifier||Primary Meaning||Additional Meanings|
|public||Method to be called by external clients of the API.||Can be overridden by subclasses. Can also be called by subclasses.|
|public abstract||Method has to be implemented by subclasses.||Method can be called by external clients.|
|public final||Method to be called.||None.|
|protected||Method can be called by subclasses.||Method can be overridden by subclasses.|
|protected abstract||Method needs to be overriden by subclasses.||None.|
|protected final||Method can be called by subclasses.||None.|
Source: Practical API Design, p. 172ff
Having only one meaning makes the intention clear. There is only one reason for the existence of a method.
In her keynote The Power of Abstraction at QCon 2013 Barbara Liskov speaks about (beside other very interesting stuff) inheritance having two purposes (starting from minute 38, if you want to jump right into it):
- Implementation Technique
- Type Hierarchy
You can see inheritance as a way to provide a skeleton that indicates clearly, what has to be implemented to get things done faster. Basically you provide a type with a couple of methods to be implemented or overridden by subtypes and the task is done.
The second reason to use inheritance is again a design principle called Liskov Substitution Principle:
Objects of subtypes should behave like those of supertypes if used via supertype methods.Barbara Liskov. The Liskov Substitution Principle
These two reasons conflict with each other. Where following the view of inheritance as an implementation technique is stressing the syntax, software development regularly heads into trouble, if it does comply with the second reason – which is purely semantic one. That is the reason why Barbara Liskov’s finding is the L in SOLID and a principle that is valued highly.
For this reason, reducing the possibility for conflicts, it would be better, if we had only one reason for selecting inheritance.
In a context where we are overwhelmed by alternative paths, it is a great relief to reduce our freedom of choice. At least as long as the one alternative that is most appropriate does not get cut off too early. But on the other side: a less optimal solution is often suitable enough in many circumstances. Getting things done cleanly is the way to go and deal with any technical dept as we have the knowledge to find a more suitable solution.
Trying to emphasis the notion of one reason is a benefit of any design. It leads us to solutions that are easier to understand, reduce the risk of doing something wrong and remove the danger of wasting time on decision making.
James O. Coplien. (May 10, 2013 at 9:11 pm)
Robert. (December 31, 2014 at 2:15 pm)