Primitive Obsession, Feature Envy & Message Chains: the “original sins” of Code Smell
The wise man says:
Prevention is better than cure and, among the various problems, even better is to prevent first those that in the long run can generate new problems.
The code smells are one of the main concepts of object-oriented programming; in particular, in this piece, I want to talk about the three “original sins”.
In addition to the classic duplication, there are three smells that should be highlighted over the others because they are the most dangerous; the reason? with time they become the cause of new smells.
I’m talking about Primitive Obsession, Feature Envy and Message Chains.
Primitive Obsession is a smell in which we have made excessive use of primitives. We should not use the language’s primitive types as parameters and return types of methods. Remember object calisthenics? One of the rules was, “Wrape all primitives,” just to prevent this smell. It’s important to create an abstraction for every data that is passed between class messages: this avoids a dependency on the primitive and also allows to encapsulate all the logic related to that data.
The classic example of this problem is the design of Money concept: we could create a variable money of type int, but the correct solution is to create a class Money; in this way, we abstract the primitive type (which could also become float if we wanted to manage decimals or even a group of variables of various types to manage the currency, for example, all without changing the type of the variable that would remain a Money). In addition, any money-related logic such as converting between two currencies also finds a natural place in this object.
Not recognizing and preventing Primitive Obsession can lead to smells such as Duplicated Code, Divergent Change, Shotgun Surgery and Long Parameter List.
The second fundamental smell to avoid is Feature Envy, in which a class A uses methods or properties of a class B excessively. For example, when class A contains a state and class B uses that state to do something, class B suffers “feature envy” of class A because B’s behavior should be in A.
This brings us to a fundamental rule: the behavior must always stay close to the state it uses.
Usually the “envied” class is a Lazy Class, like in our example the class A that contained only the state. Moreover if the behavior resides far from its reference state it can cause Shotgun Surgery because a change would cause modifications in various points; or can lead to a God Class with Divergent Change, because the class where the behavior resides risks to be modified too much for too many reasons.
Not recognizing and preventing the Feature Envy can lead to many other smells, to name a few other than those already mentioned we risk Middleman, Data Clumps and Large Class.
The third smell to highlight is the Message Chain; this smell fully represents the concept of coupling and is closely related to the Law of Demeter, that suggests to make an object communicate only with objects directly connected to it. In other words, we can only play with toys that we own, that we have built ourselves or that are given to us.
This smell must absolutely be avoided and to do so we can refer to a sort of checklist according to which messages from an object should be sent only to objects that fall in this list:
- objects contained in attributes of itself or of one of its superclasses
- objects passed as parameters to the method
- objects created by the method
- objects contained in global variables (but we don’t use global variables, don’t we..?)
Not recognizing and preventing the Message Chain can lead to smells like Data Clumps and Duplicated Code.
Here we are at the end of this detailed analysis of these three code smells that are considered the most dangerous to avoid. Remember though that the important thing is to know how to recognize them because sometimes we have to accept a smell: design is a matter of compromise, the important thing is that they are conscious and not accidental.