All posts
Oct 23, 2023 in Refactoring3 min
Nudging your refactoring tool using the type system
Posted by
Ragunath Jawahar
Ragunath Jawahar
@ragunathjawahar

Refactoring tools and their underlying algorithms have come a long way and are reasonably battle-tested. However, there may be a situation that you run into where the automated refactoring action cannot complete the intended transformation. In such scenarios, a little nudge from the type system can get you over the bump.

Here is one such example,

A when block selected for extraction
Fig. 1: A when block selected for extraction

The code snippet above shows a when block that contains code we want to extract to a new function.

However, after using the 'Extract Method…' refactoring action, the extracted when block inside the new getDuration function has a compilation error.

Extracted getDuration function showing a compilation error on line 506
Fig. 2: Extracted getDuration function showing a compilation error on line 506

Let's take a moment to understand what happened. The key to remediating this situation lies in lines 505 and 506.

storyPost.content is StoryPost.Content.TextContent -> content.calculateDurationForText()
storyPost.content is StoryPost.Content.AttachmentContent -> content.calculateDurationForAttachment()
Fig. 3: Lines 505 and 506 for a closer look

From the snippet above, we know that the storyPost.content type can be either TextContent or AttachmentContent. After performing the suitable type checks, we call the desired methods on the content object.

It also means that TextContent and AttachmentContent have at least one ancestral type in common.

The second parameter type marked as TextContent instead of the common ancestral type
Fig. 4: The second parameter type marked as TextContent instead of the common ancestral type

But, if you pay attention to the second parameter type in the extracted getDuration function, it is marked as TextContent. This type mismatch is what has caused the compilation error on line 506.

The 'Extract Method…' refactoring algorithm used the type information from line 505 for the second parameter. If the algorithm also considered the type information from line 506, it would have used the ancestral type and completed a clean extraction.

Providing type information for remediation

Since the extraction did not go through, we will discard all the changes and try a different approach. We know the 'Extract Method…' algorithm needs the information of the ancestral type. If we can somehow provide this information to the algorithm, it should be able to make a safe transformation.

'Introduce Variable…' to the rescue

Selecting the storyPost.content expression for 'Introduce Variable…'
Fig. 5: Selecting the storyPost.content expression for 'Introduce Variable…'

Let's add an intermediate transformation before we try to extract the function again. We'll first select the storyPost.content expression (line 427) and extract it as a new variable.

The newly introduced content variable with the common ancestral type (line 424)
Fig. 6: The newly introduced content variable with the common ancestral type (line 424)

The newly introduced content variable (line 424) has the common ancestral type StoryPost.Content. This additional type information should help the 'Extract Method…' algorithm.

Now, try selecting the when block again and perform the 'Extract Method…' action.

Selecting the when block again for extraction
Fig. 7: Selecting the when block again for extraction

This time, the extraction was completed cleanly without any problems.

Newly extracted getDuration function without compilation errors
Fig. 8: Newly extracted getDuration function without compilation errors

Look at the second parameter type in the extracted getDuration function. In this case, you'll also notice that the 'Extract Method…' algorithm has picked the right type after the 'Introduce Variable…' refactoring.

The getDuration function with the correct ancestral type
Fig. 9: The getDuration function with the correct ancestral type

Conclusion

While modern refactoring tools are powerful and often reliable, they may still encounter scenarios where they fail to make the intended transformation. In such instances, understanding the underlying problem is crucial. As demonstrated in this example, by introducing an intermediate transformation, we provided the tool with the necessary type information, enabling a clean extract method transformation.

This example highlights the importance of a deep understanding of the code and the tools at one's disposal. Sometimes, a slight modification or a gentle push in the right direction can make all the difference in achieving the desired outcome.

Want more insights like this? Dive deeper into working with legacy code with our articles on best practices, tips, and tricks. Subscribe to our blog and stay updated!

Overall, how helpful was this article?
😭🙁🙂😍

Stay ahead of the curve! 🚀

Subscribe now and never miss our cutting-edge, innovative content.
Address
Legacy Code Headquarters (OPC) Private Limited,
L-148, 5th Main Road,
Sector 6, HSR Layout,
Bengaluru, Karnataka-560102,
India.
© 2023–2024 Legacy Code HQ. All rights reserved.