Legacy Coderetreat: Part 12 – Extract class

Extract Class

Blog post series

This blog post is part of a series about legacy coderetreat and legacy code techniques you can apply during your work. Please click to see more sessions about legacy code.

Purpose

When working with existing code we need to look at the responsibilities of existing classes. If one class is too big, we need to extract new classes from that initial class. A class too big means that it’s not respecting the Single Responsibility Principle.

Extract Class

Extract Class

Concept

The reasons for extracting a class are mainly because:

  • The code is not testable
  • The code is complicated to test so we want to test it independently
  • The class does not respect the Single Responsibility Principle

In one of the last sessions we learned how we can extract pure functions from the production code. If we would have continued extracting other pure functions, the code would have looked like this:

The names of the two functions are giving me a hint that they could be grouped together. In order to have a clearer situation we need to rename the functions, to emphasize the relation between the two.

Step 1: Rename the methods to have noun+verb+situation in their structure

What is interesting here is that we can see how the name of the method gives me insight about what it is doing. We can certainly envision having now a class called PlayerMessage with a method createWhenNewPlayerAdded.

The fact that there is a clear duplication of the first two nouns in the method name is a clear hint that the two functions probably should be together in the same class. Another hint is that the parameter name starts with player. When I extract a new class, I will add two fields to it: name and number.

But we have only two functions that have a duplicated name. We want to respect the rule of three to be sure we have enough proof that the code really should be moved together.

Step 2: Find another occurrence to respect the Rule of Three

What we need to do next is to extract the third pure function, but only a pure function that is related to the two already extracted pure functions. For that, we look more into the production code. I found another occurrence of this type of message in the method wrongAnswer below.

Step 3: Make duplication obvious, with the Rule of Three

We can now extract another method from wrongAnswer, and the code would look like this:

Now we have a clearer view that we need a PlayerMessage class. But we need to make sure, and in order to avoid mistakes there is this guideline: always move the functions one after the other before extracting them.

Step 4: Move the three functions one after the other

After moving the functions the code would look like this:

As a rule of thumb is a good idea to move all the three functions one after the other in the initial class.

Now we have three methods that have the nouns PlayerMessage repeating in their name. Their “expertise” is in the same area. Respecting the rule of three we now can extract the three pure functions into a new class.

Step 5: Create new class

This step is somehow easy to do with an IDE that has powerful refactoring tools. If you work in a language or an environment where you do not have an Extract Class automated refactoring, I recommend you to use the steps of refactoring as explained by Martin Fowler in his book Refactoring.

 Step 6: Move static methods to new class

When moving the methods to a new class, any IDE would require that the methods are static.

Please observe that while moving the methods, my IDE removed their private signature. The methods are called like this in the production code:

 Step 7: Minimize duplication

But now we have duplication between the name of the class and the noun in each of the three pure functions. So we need to rename all the three methods, by removing the nouns.

The three methods are still static, but we will see next what refactoring we need to do in order to make them part of the object.

Step 8: Test that the production system still works

You can do this with the batch of automated tests you already have (system, component, unit, etc), but you should also do some manual testing.

Outcomes

We started with some code where the responsibilities were tangled and unclear. We focused on one functionality: creating messages that are printed at the console. By doing that we made the responsibility clear, by extracting it to several functions. All of these functions are related by the action they are doing. Each function has a single responsibility, so we respect the Single Responsibility Principle. We extracted these functions to another class. That class still has static functions, but we will soon make them functions related to the object PlayerMessage and change their signature to public. The last step would be to cover these three functions with unit tests.

By dividing the large class into smaller methods, we can split it into smaller classes that are easier to understand and test. The purpose of the refactoring Extract Class is to be able to extract behaviour from a big ugly messy class to something smaller with the purpose of maximizing changeability. And of course, with the purpose of maximizing testability.

Remarks

When dealing with legacy code, we need to make sure we do not extract too many classes. When touching the legacy code we need to focus on our purpose: add a feature, fix a bug or make the system testable.

Add a feature

If our purpose is to add a feature, then we need to extract as few classes as we can in order to make room for the new feature. No more and no less. The more we touch the old code base, the higher the probability that we will introduce defect. We also spend too much time of economically unnecessary refactorings.

Fix a bug

If our purpose is to fix a bug, then we need to find the bug, extract the class, test it and then fix it. We should not refactor more than needed. Often in an area where bugs are, the probability of introducing new ones through incomplete understanding of the code is quite high.

Make the system testable

If your purpose is to make the system testable, then we really need to extract many many classes, until the moment anything can be tested in isolation with unit tests. Even like that, we need to understand when we should stop and decide when and how we want to use the Single Responsibility Principle. Maybe it does not make sense from an economical point of view to always respect SRP, if the code can be tested.

History

The refactoring Extract Class was first explained by Martin Fowler into the book Refactoring.

Code Cast

Please find here a code cast in Java about this session

Acknowledgements

Many thanks to Thomas Sundberg for proofreading this post.

Subscribe

If you want to receive an email when I write a new article, subscribe here:

Subscribe for new articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Post Navigation