The book Facilitating Technical Events is for technical trainers, coaches, events organizers who want to have a good event flow and happy attendees.

Refactor conditionals: Explain Variable

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 you try to read a code base that has many conditionals, often the problem is that the condition itself is very hard to understand. This technique improves the conditionals by making them clear and very easy to read.

Concept

When you have some code like the one below you often wonder what is going on there. And this is a nice example, I have seen many code bases that are worse than that.

Code with many unclear conditionals

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

publicvoidroll(introll){

System.out.println(players.get(currentPlayer)+" is the current player");

System.out.println("They have rolled a "+roll);

if(inPenaltyBox[currentPlayer]){

if(roll%2!=0){

isGettingOutOfPenaltyBox=true;

System.out.println(players.get(currentPlayer)+" is getting out of the penalty box");

System.out.println(players.get(currentPlayer)+"'s new location is "+places[currentPlayer]);

System.out.println("The category is "+currentCategory());

askQuestion();

}

}

We often lose so much time understanding conditionals that are in the best case cryptic. And even worse, sometimes we think that we understand some of the conditions, we change the code and we introduce defects. A very good thing to do is to start improving the way conditionals look in your code base. One way of doing this is to extract all the conditions of an if or while statement into a variable. This kind of refactoring is called Explain Variable. But before that we need to understand really well what the code does. Misleading code is a lot worse than hard to read code.

Step 1: Understand the meaning of the condition

Java

1

if(inPenaltyBox[currentPlayer]){

Let’s take this condition for example. It is quite clear what is does: it verifies if the current player is in the penalty box from the Trivia game. But it is not very easy to read, because the flow is inverse: I read inPenaltyBoxCurrentPlayer, which is strange for English. If I want to make my reading of the code simpler, I need to extract this condition to something easier to read. A normal version for English would be currentPlayerIsInPenaltyBox.

Step 2: Extract one variable from conditional

Java

1

2

Booleanfoo=inPenaltyBox[currentPlayer];

if(foo){

If you have an IDE that knows how to extract a variable automatically, this step is no longer needed. But if you need to perform this refactoring manually, I recommend you to extract the variable, check the code works and only after that go to step 3.

Step 3: Name the extracted variable appropriately

Java

1

2

BooleanisCurrentPlayerInPenaltyBox=inPenaltyBox[currentPlayer];

if(isCurrentPlayerInPenaltyBox){

Step 4: Verify the code works well As always, after any refactoring we need to run the tests and to make sure that we did not introduce any defects.

Let’s take another example, from the same initial code base.

Step 1: Understand the meaning of the condition

Java

1

if(roll%2!=0){

This kind of verifications always annoyed me. They are often easy to misunderstand if you read them fast. What does this code mean? Usually this means that a number is odd.

Step 2: Extract one variable from conditional

1

2

BooleansomeRollCondition=roll%2!=0;

if(someRollCondition){

This time I did not extract the variable to a name like foo, I gave it a clearer name. But still, the name is not good enough.

Step 3: Name the extracted variable appropriately

1

2

BooleantheDiceValueIsOdd=roll%2!=0;

if(theDiceValueIsOdd){

Now this is a piece of code that I can read fast and without thinking for a couple of seconds or more what it does.

Step 4: Verify the code works well As always, we need to run the tests to make sure we did not introduce any issues.

Great, we explained another conditional with a variable.

But there are more conditionals to be improved.

What is this code doing? That one is a bit tougher to understand. We compare the places of the current player to a value of 11. If the places of the current player are greater than 11, that means that the places of the current player go back 12 places. What???

My unclarities:

What is places?

What is 11? What does it mean?

What is 12? What does it mean?

Why does the code go to places – 12?

Step 2: Extract one variable from conditional

This is just a mechanical step, as I said before. Do this step, and check the code works well. It is a lot safer to take baby steps and correct mistakes as soon as you make them.

Java

1

2

BooleansomeBoolean=places[currentPlayer]>11;

if(someBoolean)places[currentPlayer]=places[currentPlayer]-12;

Step 3: Name the extracted variable appropriately

Here I have a problem. I do not know how to name it well. So I prefer naming it in such a way I can express my lack of knowledge about the domain.

I know the variable looks ugly, it does not respect the rules of naming in Java, and that is exactly why I want it to look like this. If it does not respect the naming rules, it is more likely to annoy me and so I want to give it a better name. This is a lot better than just giving it a generic or a misleading name.

Step 4: Verify the code works well

As always, we need to make sure the code still works well. Compile, run all the automated tests, run manual tests and only after that make the final commit, at least for now.

It is clear that we need to find out further about the proper name in the current example.

Outcomes

We started with a method that had many unclear conditionals, and at the end we have a method that is easier to read. We want to do these kinds of changes whenever the conditional is not clear.

Now the code is easier to understand, and so we minimize the probability of making mistakes and introducing defects into the code.

In some cases we just find unknowns, as in the last example. It is a good thing to mark them as unknowns and continue refactoring. Make a list with such unknowns and discuss them with people who know the business domain.

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

publicvoidroll(introll){

System.out.println(players.get(currentPlayer)+" is the current player");

System.out.println("They have rolled a "+roll);

BooleanisCurrentPlayerInPenaltyBox=inPenaltyBox[currentPlayer];

if(isCurrentPlayerInPenaltyBox){

BooleantheDiceValueIsOdd=roll%2!=0;

if(theDiceValueIsOdd){

isGettingOutOfPenaltyBox=true;

System.out.println(players.get(currentPlayer)+" is getting out of the penalty box");

System.out.println(players.get(currentPlayer)+"'s new location is "+places[currentPlayer]);

System.out.println("The category is "+currentCategory());

askQuestion();

}

}

Remarks

This refactoring technique can be used at any time with the purpose of giving a clear meaning to the code. It is very useful also when working with existing code that does not have tests at the beginning, but only when we start adding one or two safety nets of automated tests.

This refactoring step is not enough for the current code, we would need to extract some methods and maybe extract the validation logic somewhere else. We will do that in the following episodes.

History

As far as I know the technique of explaining variable was first documented in the book Refactoring, by Martin Fowler.