Refactoring Thoughts [3]: I can’t live if living is without you
I improvised this amazing title and then out of the blue I found out that Mariah Carey has a similar song! What a coincidence! So, even if you don’t enjoy this article you can click the link above and enjoy the song at least!
The complicated relationships are not only among people. Sometimes objects have complicated relationships themselves.
For sure, I cannot explain the reason why a person in your life once left you and told you “It is complicated! It’s not you, but me!” but I could give it a try to explain such complicated relationship between your objects.
Bidirectional association
During implementation you may have come across with following situation:
Object A may have one or many Objects of type B, but A could live without having Bs as well. Simultaneously, Object B have one or many Objects of type A and Object B cannot exists if it does not have an Object A.
Trying to be more specific imagine the relationship between a Customer and an Order. A Customer may enter the shop but the Order cannot exist if the shop has no Customers.
Another example would be a bank Account with a bank Transaction. A bank could have Accounts but for sure this bank cannot have Transaction if there are no Accounts!
Let’s stick to the first example. Here is the code we might have:
Somehow, we must associate these two like in the code below:
The counselor
The first attempt would be adding another class to do the job for the couple. So, we are going to introduce a Helper
class to help the couple solving its problems!
It seems that we have successfully made them talk to each other. But they are not behaving as we expected because every time we need to make them talk to each other they always need this Shop
class.
This seems more like a lawyer instead of counselor! Imagine that any future developer would probably not know the existence of the Shop
class and tries to do a single call of the Order
class setCustomer(Customer c)
without calling the setOrder(Order o)
of the Customer
. This will be a disaster! Those classes should not get divorce! They exist for each other! We have to find another workaround for sure!
Let them talk
First of all, remove this Shop
class. It is making more damage than good! Another thing to consider about is how the Order
s are behaving? An Order
should not change its Customer
or set its Customer
to null
. An order belongs to a customer. No matter how similar two orders are, they should never be the same. This way we lead ourselves to create a value class. In order to achieve this we should remove the setCustomer(Customer c)
from Order
.
This way we only can create an Order
with a Customer
who already exists as it is shown below:
This was a very good attempt. Can it be even better? Of course, it can. Using the code snippet from above you can still pass a null
Customer
into the Order
through the constructor ( Order o1 = new Order(null, 1);
). We want to avoid this situation. The next thing would be to hide the creation of the Order
and do it explicitly from the Customer
class. This way there will be much more secure and does the job we want to do. Here is the code with a little bit more refactoring:
If I have not missed anything, this example is exactly as it should be from the beginning!
Rules
The take away rules for this refactoring are:
- Never let another class to do another class’s job, mostly a Helper/Manager/Converter. These names are bad! Sometimes you cannot avoid them but you have think twice before using them!
- Data, which are relevant and/or change with the same rate, should live in the same department (same class or same package).
- Always start with the most restrictive modifier and change it to its closest less permissive, if necessary.
The end
And the classes lived well and prosper…