_Replace Supplier with Supplies_ and friends

Here are three very specific uses of Extract Method on a function that takes an unwieldy object as an input, to help make code cleaner and easier to test. I learned these from Arlo Belshee.

Leave Loaders Behind

Context

The first lines of the function pull some values out of the large passed-in object. The rest of the function does not access the large object again.

F(Database database)
{
    var a = database.get...
    var b = database.get...
    var c = database.get...

    // do stuff with a/b/c
}

How to do it

Extract Method, for all of the method except for the lines that extract those bits off data.

F(database)
{
    var a = database.get...
    var b = database.get...
    var c = database.get...
    F(a, b, c)
}

F(a, b, c)
{
    // do stuff with a/b/c
}

The new function gets the same name as the old function (it’s an overload), since it does the same thing.

Advanced

  • Sometimes at the end of the method, the result of a method gets packaged into a large object to be returned. Sometimes you can leave that result-packing behind in the same way.

  • Inspect callers; some will be happier calling the new overload.

  • Consider whether a subset of parameters relate to each other in a way that would make sense as a new object (Extract Parameter Object). Then consider moving this new method onto the new object.

Replace Supplier with Supplies

Context

Similar to Leave Loaders Behind, the function only uses a subset of the values in an object that is passed in, but not all at the top of the function.

F(Database database)
{
    // some code...

    var a = database.get...

    // more code...

    var b = database.get...

    // still more code

    var c = database.get...

    // yet more code...
}

How to do it

First incrementally refactor to bring all the object reads to the top of the function

F(Database database)
{
    var a = database.get...
    var b = database.get...
    var c = database.get...

    // some code...

    // more code...

    // still more code

    // yet more code...
}

Then apply Leave Loaders Behind in the same way.

Be sure to verify that reordering these statements doesn’t change behavior, for example if the retrieved value changes as the function executes.

Replace Suppliers with Thunks

Context

Similar to Replace Supplier with Supplies, but you can’t be sure that retrieved values aren’t changing, or the function is calling a setter or other state-modifying code.

How to do it

Wrap each troublesome statement/expression in an immediately-executed lambda:

F(Database database)
{
    // some code...

    var a = (() -> database.get...)()

    // more code...

    var b = (() -> database.get...)()

    // still more code

    var c = (() -> database.get...)()

    // yet more code...
}

Then Extract Local Variable on these lambdas:

F(Database database)
{
    // some code...

    var getA = (() -> database.get...)
    var a = getA()

    // more code...

    var getB = (() -> database.get...)
    var b = getB()

    // still more code

    var getC = (() -> database.get...)
    var c = getC()

    // yet more code...
}

Then reorder statements:

F(Database database)
{
    var getA = (() -> database.get...)
    var getB = (() -> database.get...)
    var getC = (() -> database.get...)

    // some code...

    var a = getA()

    // more code...

    var b = getB()

    // still more code

    var c = getC()

    // yet more code...
}

Then extract method:

F(Database database)
{
    var getA = (() -> database.get...)
    var getB = (() -> database.get...)
    var getC = (() -> database.get...)

    F(getA, getB, getC)
}

F(getA, getB, getC)
{
    // some code...

    var a = getA()

    // more code...

    var b = getB()

    // still more code

    var c = getC()

    // yet more code...
}

It’s OK to name the new function with an _Impl suffix and keep it private, if you don’t think it’s a natural API to be exposing from this object. Consider both options.

Advanced

A subset of these lambdas might become methods on a new object, possibly a facade.

Written on March 18, 2020