Let’s talk about refactoring again: How can you move some code to different value objects using small, safe steps? In this video, I want to show you exactly which refactoring steps I take to extract a factory method, move it to one value object and move some code around it to another value object.

In my last video about “Refactoring Primitive Obsession”, I did all that very quickly to give you an overview about the whole process. I have added a link to that video in the description.

Today I want to show you a small step from this process and explain exactly what I did.

Intro (TL;DR)

My name is David and I am a trainer and technical agile coach with over 12 years of experience. In my videos, I want to show you techiques and tricks that I use and that I also teach when training or coaching teams.

Today I want to try something new: A very short video that focuses on a single thing. In this case, one refactoring step.

By the way, if you like this video, please subscribe to my channel and share it with your friends and followers - That would be awesome!

Question / Problem

In the code I want to start with, I already created a type for TaxAccountNumber but it does not do anything yet. It only wraps a single string.

TaxAccountNumber taxAccountNo = TaxAccountNumber
    .fromString(taxAccountNoUnvalidatedUserInput);
String taxOfficeIdInput = taxAccountNo.asString().substring(0, 2);
Integer taxOfficeId = Integer.valueOf(taxOfficeIdInput);

Now, I want to create a new type for the taxOfficeId, complete with a factory method, and move the code for extracting the first two digits and creating the taxOfficeId to TaxAccountNumber.

Solution

First, I create a class TaxOfficeId that wraps the taxOfficeId integer. I create a new variable, taxOfficeIdObject, for it and replace all usages of the integer with the new object.

TaxAccountNumber taxAccountNo = TaxAccountNumber
    .fromString(taxAccountNoUnvalidatedUserInput);
String taxOfficeIdInput = taxAccountNo.asString().substring(0, 2);
Integer taxOfficeId = Integer.valueOf(taxOfficeIdInput);
TaxOfficeId taxOfficeIdObject = new TaxOfficeId(taxOfficeId);

TaxOffice taxOffice = taxOfficeRepository.findByNumber(taxOfficeIdObject.asInteger());
if(taxOffice == null) {
    payTaxesView.showTaxOfficeError(
        "The tax office \""+taxOfficeIdObject.asString()+"\" does not exist.");
} else {
    //...
}

I can extract the two lines that convert the ID string to an integer and create the object to a new method on the controller class, named fromString. When I make this method static, I can then move it to the class TaxOfficeId.

TaxAccountNumber taxAccountNo = TaxAccountNumber
    .fromString(taxAccountNoUnvalidatedUserInput);
String taxOfficeIdInput = taxAccountNo.asString().substring(0, 2);
TaxOfficeId taxOfficeIdObject = TaxOfficeId.fromString(taxOfficeIdInput);

As a last step, I can now extract the two lines that get the sub-string and create the tax office ID to another method and move it to the class TaxAccountNumber. So, I first extract a method from the two lines, then I move it and last I inline the redundant call to asString in the moved method.

I can also rename taxOfficeIdObject to taxOfficeId again, because there now is no name clash anymore.

TaxAccountNumber taxAccountNo = TaxAccountNumber
    .fromString(taxAccountNoUnvalidatedUserInput);
TaxOfficeId taxOfficeId = taxAccountNo.getTaxOfficeId();

Conclusion

The tax office ID is a part of the tax account number (encoded in the first two digits), so it makes sense to have the code to extract and create it on the class TaxAccountNumber.

The code I created with this refactoring now reflects the domain concept of a tax account number much better than the old version.

I was either adding code or using built-in IDE functionalty to transform existing code. Both operations are reasonably safe. And I ran the tests after every step. This allowed me to change the code and be very sure that I did not change any functionality.

CTA

What do you think? How would you refactor this code? Please tell me in the comments on youtube or on Twitter, where I am called @dtanzer.

And if you liked this video, please subscribe and share it with your friends and followers (using the share buttons below) - That would be awesome!