3. Couplers
Pairs = Dependency = Suffocation due to excessive attachment.
Let go! Freedom is a birthright.
3
Message Chains
Middle ManFeature Envy
Inappropriate Intimacy
4. FEATURE ENVY
An object accesses the data of another object more than its own data
Class A
X
Y
Class B
R
S
MethodXY()
Class A
X
Y
MethodXY()
Class B
R
S
High coupling between classes
Poor organization of code
Increased code duplication
5. TELL! DON’T ASK.
Method accesses data of another class more than its own data?
Move Method
Only a part of method accesses data?
Extract Method -> Move Method
6. INAPPROPRIATE INTIMACY
Classes spend too much time delving into each other’s private parts
High coupling between classes
Loss of encapsulation
Exposing private fields = Exposing implementation details
7. Two independent classes are entangled (Method/fields of one class used by another class frequently
and vice versa)?
Move Method
Move field
…..to put right pieces on the right class
Not able to move the tangled part?
Extract Class -> Hide Delegate
Class delegates many of its methods to another class?
Replace Delegation with Inheritance
Two interdependent classes are entangled?
Change Bidirectional Association to Unidirectional
Subclass knows about its parent more than the parent would like it to know (subclass uses only part
of a superclass interface or does not want to inherit data)?
Replace Inheritance with Delegation
8. MESSAGE CHAIN
Your code contains series of calls like obj.m1().m2().m3().m4()
High coupling between classes
Complex data access
Client is coupled to the structure of the navigation
a.getB().getC().getD().getTheNeededData() a.getTheNeededData()
9. LAW OF DEMETER
Only talk to your friends
Only ask for objects you directly operate on
Delete message chain -> Hide Delegate
10. MIDDLE MAN
A class delegates majority of its work to another class
Can be the result of overzealous elimination of Message Chains
Class A is like an empty shell that does not do anything
other than delegating to Class B
A shell has no worth of its own!
KEEP THE PEARL, THROW THE SHELL!
Remove Middle Man
Bulky code
Unnecessary complexity
Additional cost due to extra class
This presentation is continuation to “Software Craftsmanship – Code Smells_2_Bloaters”. Here we will cover the third category of code smells – “Couplers”.
How do we know that code we have written is good or bad? Is it a subjective opinion or is there any metrics to determine whether it is readable/extensible/maintainable?
Luckily for us, there IS A METRIC!
When you go past a garbage can, you tend to block your nose, as the foul smell is almost unbearable. When there is something wrong with the code, we say there are certain elements in it which stink, making the whole code stink as well! These stinky elements are referred to as code smells. Now, when it comes to real life objects, the sense of foul smell is an innate human tendency. But when it comes to code, we need to teach ourselves to identify these code smells.
In other words, we need to develop a nose to identify foul smells in the code. And this ability can only be developed when you are relatively young (new to development). As soon as a hatchling starts flying, its mother teaches it to hunt, for it won’t survive in its adulthood without the ability. The same goes with the ability to smell stink inside the code. It becomes harder and harder to develop this ability as we grow further into software development without it. For it is easier to learn than to unlearn. Writing a code with wrong practices is more dangerous than not writing the code at all! IT IS VERY IMPORTANT FOR ANY YOUNG DEVELOPER TO DEVELOP A NOSE FOR THESE CODE SMELLS!
There are broadly 5 categories code smells are divided into. We are covering the third category here: Couplers
Tie a man with a chair and tell him to run. The man will barely move let alone run! This is because the man is coupled tightly with the chair. Also, is the chair of any use now? To help the man run or to use the chair, you need to untie the rope!
Same happens to your classes which are tightly coupled. Either they will not function freely or they will become obsolete.
All the elements in your code that act like a rope coupling your classes and suffocating them with dependencies fall in the category of code smells knows as COUPLERS.
Lets explore these elements in detail.
Feature envy happens when there is a class that contains some data (Class A) and another class contains behavior on this data (Class B). Every time Class B wants to execute this behavior, it needs to ask Class A to provide its data. Overtime, Class B becomes envious of Class A’s features as it cannot perform its behavior without these features.
Class B is dependent (coupled) on Class A for its behavior and a smell in introduced!
Also, notice how dumb Class B is! (Remember Data Class code smell?)
This often also results in code duplication. How? Consider this:
In the figure, the fruits are present in one class but the method to cut the fruits (with knife) is present in another. Now if another class wants to cut the fruits, the same method will also have to be copied inside that class.
As discussed previously (when covering Dispensables), object oriented programming is bringing behavior closer to the data.
Remember this principle – “Tell! Don’t Ask”. Don’t ask data from the objects and work on this data. Rather tell the objects to perform the work for you. Don’t be a slave of the object. It should be the other way around!
Either the behavior acting on the data should be moved to the class containing the data or the data should be moved to the class containing the behavior (Mostly, its always the first scenario as some other classes might be dependent on the data class as well).
Treatment
Use Move Method to move method into the data class
If only part of a method accesses the data of another object, use Extract Method to move the part in question.
If a method uses functions from several other classes, first determine which class contains most of the data used. Then place the method in this class along with the other data. Alternatively, use Extract Method to split the method into several parts that can be placed in different places in different classes.
Would you want someone to have access to your private parts without your permission? Isn’t it humiliating if someone tries to do so?
Why let your classes suffer the same humiliation due this inappropriate intimacy!? You humiliate your classes by providing others a direct access to its private variables.
One solution is to then create Getters and Setters, right? HELL NO!
Why? Because its like safeguarding your cash by using a world class locker and keeping the keys of the locker on top of it. No one can access your cash without opening the locker, but can certainly use the key provided to open it! The locker here is your “private” keyword and getters and setters are your keys to the locker. Makes sense now?
STOP HUMILATING YOUR CLASSES BY CREATING INAPPROPRIATE INTIMACIES!
Remember this –”Getters and Setters are evil”. Try avoiding them.
How to avoid them? The answer is very simple and has been told a lot of times by now.
Move behavior closer to your data! Why to ask an object for its data and work on the data? Instead tell the object to do the work for you.
Follow this and you won’t require getters and setters every again in your code!
Treatment
(Read the slide)
Lets go back to your school days. You are sitting on the first bench. You want to communicate an important but complex message to your friend sitting on the last bench. Instead of standing up and going to him directly, you decide to tell the person sitting on the second bench to help your out as he is closer to your friend than you are. The person sitting on the second bench does the same asking the person on the third bench to help him out! And this goes on…!
What are the chances that the message delivered is as it is? That it maintains its truth.
Are you not indirectly dependent on all the people sitting between you and your friend for this communication? What if someone decides to exchange his seat with a person in the adjacent row who is unware of what is going on!?
If you have a series of method calls in your code resembling a->b()->c()->d(), chances are that your data is suffering a similar fate.
You are introducing a smell in your code by using message chains!
Any change in the object participating in the chain requires you to modify the client as well!
Stand up and go to your friend directly and communicate the message.
Now, what if person on the last bench is not your friend, but only the person on the second bench is your friend and you want to communicate the message to the stranger on the last bench. What will you do? Direct communication?
NO. Remember this – “Talk only to your friends”. Do not trust a stranger. This is the LAW OF DEMETER.
Ask your friend on the second bench to do it for you. Who in turn should ask his friend on 3rd bench to do the same. Only ask for objects you directly operate on.
Treatment
To delete a message chain, use Hide Delegate
There is a degree exam going on. You enter the exam hall with a friend who doesn’t study in college. The invigilator asks you the reason for bringing your friend. You tell him – “My friend is the one who studies. He answers and I write it down.”
Your friend is the mediator – the middle man doing all your tasks.
If a class delegates all its functions to another class, which does most of the work, the second class is called a middle man. The first class is of no use literally and is just present to bloat your code and add to its cost.
Should your college continue keeping you as one of the students? Is the first class of any use? What should be done in such a case?
Simple solution: “Throw out everything worthless!”. The first class should be deleted from the code and second class be used directly to preform functions.
Your friend should be given the degree, and you should be thrown out of the college
Treatment
If most of a method's classes delegate to another class, Remove Middle ManA class has too many methods that simply delegate to other objects? Delete these methods and force the client to call the end methods directly