You didn’t think we’ve covered everything, did you? Here’s a short reminder of what we got through:

The Legacy Code To Testable Code Series

General patternsAccessibilityDealing with dependenciesAdvanced patterns
IntroductionAdd settersExtract methodStatic constructors (initializers)
RenamingMore accessorsExtract classMore static constructors
Add overloadIntroduce parameterInstance constructors
Testable objectConditionals to guard blocks

Today we’re going to talk about the Introduce Parameter refactoring pattern. This is one of the more useful patterns to clear code from dependencies.

Here’s a method we’d like to test:

public int Calculate(int x, int y)
{
    if (OperationManager.ShouldAdd())
        return x+y;
    else
        return x-y;
}

As we can see, there’s a nasty dependency right there in the middle. Since in this example the static ShouldAdd call returns a Boolean result, we can turn this result into a parameter:

public int Calculate2(int x, int y, bool shouldAdd)
{
    if (shouldAdd)
        return x + y;
    else
        return x - y;
}

Thus removing the necessity to mock the static call, or handle side effects with the static calls (hello there, Mr. Singleton!).

Now, if the static call returns an object, the same thing applies.

public int Calculate(int x, int y)
{
    if (OperationManager.Instance.ShouldAdd())
        return x+y;
    else
        return x-y;
}

This time, we’ll transform the singleton instance into a parameter:

public int Calculate(int x, int y, OperationManager opManager)
{
    if (opManager.ShouldAdd())
        return x + y;
    else
        return x - y;
}

This case may not be as trivial as passing a Boolean value. Now we need to mock that object, which may be a problem, for instance (pun intended), if our OperationManager has a private constructor. In this case we can now introduce an interface we can mock:

public int Calculate(int x, int y, IOperationManager iopManager)
{
    if (iopManager.ShouldAdd())
        return x + y;
    else
        return x - y;
}

We still need to do all the modifications, but hopefully we have refactoring tools to help us.

Cleaning up dependencies not only helps us to test directly, but also makes it easy to extract the method into a new class.

Field extraction

An interesting case is when we have a field, being used inside the method:

Boolean shouldAdd;

public int Calculate(int x, int y)
{
    if (shouldAdd)
        return x+y;
    else
        return x-y;
}

That shouldAdd field is a dependency. It may not look like it, but it really is. You will notice it, when you try to extract the method into a separate class.

When we need to get rid of field dependencies, we can use the same refactoring pattern. This is a transitional step on the road to extraction to a separate class.

public int Calculate(int x, int y, bool shouldAdd)
{
    if (shouldAdd)
        return x+y;
    else
        return x-y;
}

Note that while the parameter has the same name as the field, its scope has changed, and the method now uses it, rather than the field.

 


0 Comments

Leave a Reply

Avatar placeholder

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