At UserTesting, refactoring code is a way of life. In this third installment of "Smell a Smell," learn to identify and refactor couplers.
In the spirit of investigating the “coupling” code smells, we will be looking at smells that are often found together — specifically Feature Envy and Message Chains. We will also make references to the other smells, Inappropriate Intimacy and Middle Man. We will look at an example and work through refactoring it one step at a time. Imagine we have integration tests running in the background, because we always refactor using tests.
To identify feature envy, Look for methods that are accessing data belonging to another object more frequently than it’s own. Here is a small example - notice how the only data accessed is that belonging to the yard?
Message chains are one of the more common smells in Rails applications. Long sequences of messages linked together are a sure sign you need to look at your dependencies. In Ruby it can look like:
Step one: Identify the smells and break them down
We will start by looking at some simplified real world code from the UserTesting codebase.
Assume that we have orders, which occasionally need to be refunded. To generate the refund, we need to know how much the refund is worth.
Step two: Move that Method!
Now that we know what to look for, it should be obvious that the new_price_per_test method is suffering from Feature Envy, a minor case of Inappropriate Intimacy, and just a touch of being a Middle Man. Notice how the method is solely concerned with data belonging to the order - self is not referenced once in this method.
The recommended refactor when encountering feature envy is Move Method, and as a byproduct of moving the method we have also removed the middle man. It is obvious that the target of our move is the order class, as it is the object receiving every message in the method.
Step three: Break the chain into explaining variables
We now are no longer concerned with the RefundPayment class to determine the price, and the new_price_per_test method is greatly simplified by removing the need to refer back to it’s order.
This concludes our feature envy refactor, but we will continue on to address the message chain that remains in the new_price_per_test method. To do this, we will break the chain into smaller explaining variables and use these to determine our next steps.
Step four: extract methods for each explaining variable
This is better, as we have exposed the dependencies we need to address. We extract methods for each variable, one at a time, while making sure our tests remain green.
Step five: Rinse. Repeat.
With this method extracted, we will continue the same process of extracting our explaining variable into a method while making sure our tests are still passing.
Step six: Delete unused code
We have now extracted methods for each of the links in our message chain, applying the middle man refactor along the way. The final step to our refactor is to remove the now-unused code.
Though we still have dependencies within the class on prepaid_credit_payments and their credits, we have eliminated the need for RefundPayment in calculating our new_price_per_test.
Refactoring can really help with code maintainability and should be practiced regularly! To learn more about refactoring, we recommend picking up a copy of the Refactoring book. Stay tuned for the next episode and more tips!