Showing posts with label legacy. Show all posts
Showing posts with label legacy. Show all posts

testing legacy C/C++ when it resists

In my travels I'm sometimes asked to help a client write some unit-tests for C/C++ code which wasn't built with unit-testing in mind and so, naturally, is resisting being unit-tested. This is a classic chicken and egg situation; you want to refactor the code to get the unit-tests in place, but of course that's dangerous and painful and slow until you've got at least some unit tests in place. There are two big problems:
  • Repaying the legacy debt is likely to be a long and arduous road. There's not a lot I can do to help here except offer encouragement and to maybe remind them of the Winston Churchill quote
    if you're going through hell, keep going!
  • It often seems there's no way to get started. Clients might say something like "this can't be unit tested" when of course what they really mean is "I don't know how to unit test this". Sometimes I can suggest tricks and techniques.


One way to get started is to make the problem smaller. Suppose I have a large legacy C++ class resolutely resisting being unit-tested. I pick a method and start with that. For example, given this file, fubar.cpp

   1|#include "fubar.hpp"
   2|#include ...
   3|#include ...
    |...
1438|int fubar::f1() const
1439|{
    |   ...
1452|}
1453|
1457|void fubar::example(widget & w, int x)
1458|{
    |   ...
1598|}
1599|
1600|int fubar::f2()
1601|{
    |   ...
4561|}
4562|
I decide to start with fubar::example() which starts at line 1457 of fubar.cpp and ends 100+ lines later: I carefully cut all of lines 1457-1598 into its own new file called fubar-example
   1|
   2|void fubar::example(widget & w, int x)
   3|{
    |   ...
 141|}
 142|
and replace the cut lines from fubar.cpp with a single #include to the new file:
   1|#include "fubar.hpp"
   2|#include ...
   3|#include ...
    |...
1438|int fubar::f1() const
1439|{
    |   ...
1452|}
1453|
1457|#include "fubar-example" // <----
1458|
1459|int fubar::f2()
1460|{
    |   ...
4420|}
4421|
I'm aiming to create a unit-test for fubar::example() like this:
// here I'll dummy out everything used in fubar::example

#include "fubar-example"

// here I'll write my first unit test
However, as safe as it seems, this could cause a change in behaviour! I can easily check this. If fubar.cpp is one of the source files that compiles into something.lib then I can compare the 'before' and 'after' versions of this lib file to see if they are identical. They should be. One reason they might not be is because of things like the assert macro which uses __FILE__ and __LINE__ to report the filename and line-number. I've changed the line numbers on everything below the new #include and the lines numbers and filename inside the included file. I can fix that using the #line directive.

In the original fubar.cpp file example() started at line 1457 so fubar-example becomes:
   1|
   2|...
   3|
   4|#line 1457 "fubar.cpp"  // <----
   5|void fubar::example(widget & w, int x)
   6|{
    |   ...
 145|}
 146|
and the next method f2() started at line 1600 so fubar.cpp becomes:
   1|#include "fubar.hpp"
   2|#include ...
   3|#include ...
    |...
1438|int fubar::f1() const
1439|{
    |   ...
1452|}
1453|
1457|#include "fubar-example" 
1458|
1459|#line 1600 // <----
1460|int fubar::f2()
1461|{
    |   ...
4421|}
4422|
Now the before and after versions of the lib file are identical. Now I try to compile the test file:
// here I'll dummy out everything used in fubar::example

#include "fubar-example"

// here I'll write my first unit test
It fails to compile of course, since I can't define fubar::example() unless I've previously declared it. So I dummy it out:
class fubar
{
public:
    void example(widget & w, int x); // <----
};

#include "fubar-example"

// here I'll write my first unit test
Now it fails because the compiler doesn't know what widget is. So I forward declare it:
class widget; // <----

class fubar
{
public:
    void example(widget & w, int x);
};

#include "fubar-example"

// here I'll write my first unit test
Now it fails because fubar::example() calls a method nudge(int,int) on the widget parameter:
   1|
   2|...
   3|#line 1457 "fubar.cpp" 
   4|void fubar::example(widget & w, int x)
   5|{
    |   ...
    |   w.nudge(10,10); 
    |   ...
 145|}
 146|
So I dummy it out:
class widget
{
public:
    void nudge(int,int) // <----  
    {
    }
};

class fubar
{
public:
    void example(widget & w, int x);
};

#include "fubar-example"

// here I'll write my first unit test
Now it fails because fubar::example() invokes a macro LOG:
   1|
   2|...
   3|#line 1457 "fubar.cpp" 
   4|void fubar::example(widget & w, int x)
   5|{
    |   ...
    |   LOG(... , ...);
    |   ...
 145|}
 146|
So I dummy it out:
#define LOG(where,what)  /*nothing*/  // <----

class widget ...

class fubar
{
public:
    void example(widget & w, int x);
};

#include "fubar-example"

// here I'll write my first unit test
Maybe later I can return to the dummy LOG macro and make it less dumb but for now I'm not even compiling. One thing at a time.

Now it fails because fubar::example() declares a local std::string:
   1|
   2|...
   3|#line 1457 "fubar.cpp" 
   4|void fubar::example(widget & w, int x)
   5|{
    |   ...
    |   std::string name = "...";
    |   ...
 145|}
 146|
This one I don't need to dummy out.
#include <string>   // <----

#define LOG(where,what)  /*nothing*/

class widget ...

class fubar
{
public:
    void example(widget & w, int x);
};

#include "fubar-example"

// here I'll write my first unit test
Now it fails because fubar::example() calls a sibling method:
   1|
   2|...
   3|#line 1457 "fubar.cpp" 
   4|void fubar::example(widget & w, int x)
   5|{
    |   ...
    |   if (tweedle_dee(w))
    |   ...
 145|}
 146|
So I dummy it out:
#include <string>

#define LOG(where,what)  /*nothing*/

class widget ...

class fubar
{
public:
    void example(widget & w, int x);

    bool tweedle_dee(widget &) // <----
    {
        return false;
    }
};

#include "fubar-example"

// here I'll write my first unit test
Now it fails because fubar::example() makes a call on one of its data members:
   1|
   2|...
   3|#line 1457 "fubar.cpp" 
   4|void fubar::example(widget & w, int x)
   5|{
    |   ...
    |   address_->resolve(name.begin(), name.end());
    |   ...
 145|}
 146|
So I dummy it out, making no attempt to write the actual types of the parameters (a useful trick):
#include <string>

#define LOG(where,what)  /*nothing*/

class widget ...

class address_type
{
public:
    template<typename iterator>
    void resolve(iterator, iterator) // <----
    {
    }
};

class fubar
{
public:
    void example(widget & w, int x);

    bool tweedle_dee(widget &) 
    {
        return false;
    }

    address_type * address_; // <----
};

#include "fubar-example"

// here I'll write my first unit test
On I go, one step at a time, until finally, it compiles! Hoorah!

I'm reminded of a saying I heard (from Michael Stal). It's when there's a library you pull in but, on pulling it in, you find it has two further dependencies and you have to pull in those aswell. And they have their dependencies too. etc etc. The saying is:

you reach for the banana; you get the whole gorilla!

Except that sometimes it's worse than that. Sometimes...

you reach for the banana; you get the whole jungle!


Ok. So now it compiles. But I haven't written my first unit-test yet! So I start that:
#include <string>

#define LOG(where,what)  /*nothing*/

class widget ...

class address_type ...

class fubar
{
public:
    void example(widget & w, int x);

    bool tweedle_dee(widget &) 
    {
        return false;
    }

    address_type * address_; 
};

#include "fubar-example"

int main()
{
    fubar f;
    widget w;
    f.example(w, 42); // <----
}
Now I have a test I can actually run! Hoorah! There's no actual assertions yet, but one thing at a time. I run it. It crashes of course. The problem could be the address_ data member. The compiler generated default constructor doesn't set it so it's a random pointer. I might be able to fix that by repeating the same extraction of the constructor(s) into separate #included files. Ultimately that's what I want of course. But one thing at a time. I can definitely fix it by writing my own constructor:
#include <string>

#define LOG(where,what)  /*nothing*/

class widget ...

class address_type ...

class fubar
{
public:
    explicit fubar(address_type * address) // <----
        : address_(address)
    {
    }

    void example(widget & w, int x);

    bool tweedle_dee(widget &) 
    {
        return false;
    }

    address_type * address_; 
};

#include "fubar-example"

int main()
{
    address_type where; // <----
    fubar f(&where); // <----
    widget w;
    f.example(w, 42);
}
Now it compiles and runs without crashing! Hoorah! It is a horrible hack. Painful. But the gorilla is no longer so invisible! And if nothing else, I've got code reflecting the current understanding of my attempt to hack a way into the jungle! I've made a start. I've got something I can build on. And remember, when you say "X is impossible" what you really mean is "I don't know how to X".

P.S.
Here's another horrible testing hack for C/C++.

holes

is an excellent book by Louis Sachar (isbn 0-439-12845-5). If you've not seen the film the story revolves around a boy called Stanley, whose offered the choice of going to jail or going to Camp Green Lake. He chooses the lake. Each day he has to dig a hole five feet deep and five feet in diameter.
As usual I'm going to quote from a few pages, particularly the ones about digging the holes. They remind me of refactoring, and the pain of introducing a test-seam into legacy code:
There is no lake at Camp Green Lake.
Using all his might, he brought the shovel back down onto the dry lake bed. The force stung his hands but made no impression on the earth.
...
He noticed a thin crack in the ground. He placed the point of his shovel on top of it, then jumped on the back of the blade with both feet. The shovel sank a few inches into the packed earth. He smiled. For once in his life it paid to be overweight. He leaned on the shaft and pried up his first shovelful of dirt, then dumped it off to the side. Only ten million more to go, he thought, then placed the shovel back in the crack and jumped on it again. He unearthed several shovelfuls of dirt in this manner, before it occurred to him that he was dumping his dirt within the perimeter of his hole.
The digging got easier after a while. The ground was hardest at the surface, where the sun had baked a crust about eight inches deep. Beneath that, the earth was looser.
Though he tried to convince himself otherwise, he'd been aware for a while that his piles of dirt were too close to his hole. The piles were outside his five-foot circle, but he could see he was going to run out of room. Still, he pretended otherwise and kept adding more dirt to the piles, piles that he would eventually have to move. The problem was that when the dirt was in the ground, it was compacted. It expanded when it was excavated. The piles were a lot bigger than his holes were deep. It was either now or later. Reluctantly, he climbed up out of his hole, and once again dug his shovel into his previously dug dirt.
Stanley kept digging. His hole was almost up to his shoulders, although it was hard to tell exactly where ground level was because his dirt piles completely surrounded the hole. The deeper he got, the harder it was to raise the dirt up and out of the hole. Once again, he realized, he was going to have to move the piles.
As he dug he was careful to dump the dirt far away from the hole. He needed to save the area around the hole for when his hole was much deeper.
"Well, the first hole's the hardest," Magnet said.
"Well, the first hole's the hardest," said Stanley. "No way," said X-Ray. "The second hole's a lot harder. You're hurting before you even get started. If you think you're sore now, just wait and see how you feel tomorrow morning, right?" "That's right," said Squid. "Plus, the fun's gone," said X-Ray.
"You're right," he said to X-Ray. "The second hole's the hardest." X-Ray shook his head. "The third hole's the hardest," he said.
That meant he'd dug forty-four holes. Stanley dug his shovel into the dirt. Hole number 45. "The forty-fifth hole is the hardest," he said to himself. But that really wasn't true, and he knew it. He was a lot stronger than when he first arrived. His body had adjusted somewhat to the heat and harsh conditions.

Isolating legacy C code from external dependencies

Code naturally resists being isolated if it isn't designed to be isolatable. Isolating legacy code from external dependencies can be awkward. In C and C++ the transitive nature of #includes is the most obvious and direct reflection of the high-coupling such code exhibits. However, there is a technique you can use to isolate a source file by cutting all it's #includes. It relies on a little known third way of writing a #include. From the C standard:

6.10.2 Source file inclusion
...
A preprocessing directive of the form:
  #include pp-tokens 
(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text. ... The directive resulting after all replacements shall match one of the two previous forms.


An example. Suppose you have a legacy C source file that you want to write some unit tests for. For example:
/*  legacy.c  */
#include "wibble.h"
#include <stdio.h>
...
int legacy(int a, int b)
{
    FILE * stream = fopen("some_file.txt", "w");
    char buffer[256];
    int result = sprintf(buffer, 
                         "%d:%d:%d", a, b, a * b);
    fwrite(buffer, 1, sizeof buffer, stream);
    fclose(stream);
    return result;
}
Your first step is to create a file called nothing.h as follows:
/* nothing! */
nothing.h is a file containing nothing and is an example of the Null Object Pattern. Then you refactor legacy.c to this:
/* legacy.c */
#if defined(UNIT_TEST)
#  define LOCAL(header) "nothing.h"
#  define SYSTEM(header) "nothing.h"
#else
#  define LOCAL(header) #header
#  define SYSTEM(header) <header>
#endif

#include LOCAL(wibble.h)  /* <--- */
#include SYSTEM(stdio.h)  /* <--- */
...
int legacy(int a, int b)
{
    FILE * stream = fopen("some_file.txt", "w");
    char buffer[256];
    int result = sprintf(buffer, 
                         "%d:%d:%d", a, b, a*b);
    fwrite(buffer, 1, sizeof buffer, stream);
    fclose(stream);
    return result;
}
Now structure your unit-tests for legacy.c as follows:
First you write null implementations of the external dependencies you want to fake (more Null Object Pattern):
/* legacy.test.c: Part 1 */

static FILE * fopen(const char * restrict filename, 
                    const char * restrict mode)
{
    return 0;
}

static size_t fwrite(const void * restrict ptr,   
                     size_t size, 
                     size_t nelem, 
                     FILE * restrict stream)
{
    return 0;
}

static int fclose(FILE * stream)
{
    return 0;
}
Then #include the source file. Note carefully that you're #including legacy.c here and not legacy.h and you're #defining UNIT_TEST so that legacy.c will have no #includes of its own:
/* legacy.test.c: Part 2 */
#define UNIT_TEST
#include "legacy.c" 
Then write your tests:
/* legacy.test.c: Part 3 */
#include <assert.h>

void first_unit_test_for_legacy(void)
{
    /* writes "2:9:18" which is 6 chars */
    assert(6, legacy(2,9));
}

int main(void)
{
    first_unit_test_for_legacy();
    return 0;
}
When you compile legacy.test.c you will find your first problem - it does not compile! You have cut away all the #includes which cuts away not only the function declarations but also the type definitions, such as FILE which is a type used in the code under test, as well as in the real and the null fopen, fwrite, and fclose functions. What you need to do now is introduce a seam only for the functions:
/* stdio.seam.h */
#ifndef STDIO_SEAM_INCLUDED
#define STDIO_SEAM_INCLUDED

#include <stdio.h>

struct stdio_t
{
    FILE * (*fopen)(const char * restrict filename, 
                    const char * restrict mode);
    size_t (*fwrite)(const void * restrict ptr, 
                     size_t size,  
                     size_t nelem, 
                     FILE * restrict stream);
    int (*fclose)(FILE * stream);
};

extern const struct stdio_t stdio;

#endif    
Now you Lean On The Compiler and refactor legacy.c to use stdio.seam.h:
/* legacy.c */   
#if defined(UNIT_TEST)
#  define LOCAL(header) "nothing.h"
#  define SYSTEM(header) "nothing.h"
#else
#  define LOCAL(header) #header
#  define SYSTEM(header) <header>
#endif

#include LOCAL(wibble.h) 
#include LOCAL(stdio.seam.h)  /* <--- */
...
int legacy(int a, int b)
{
    FILE * stream = stdio.fopen("some_file.txt", "w");
    char buffer[256];
    int result = sprintf(buffer, 
                         "%d:%d:%d", a, b, a*b);
    stdio.fwrite(buffer, 1, sizeof buffer, stream);
    stdio.fclose(stream);
    return result;
}    
Now you can structure your null functions as follows:
/* legacy.test.c: Part 1 */
#include "stdio.seam.h"

static FILE * null_fopen(const char * restrict filename, 
                         const char * restrict mode)
{
    return 0;
}

static size_t null_fwrite(const void * restrict ptr, 
                          size_t size, 
                          size_t nelem, 
                          FILE * restrict stream)
{
    return 0;
}

static int null_fclose(FILE * stream)
{
    return 0;
}

const struct stdio_t stdio =
{
    .fopen  = null_fopen,
    .fwrite = null_fwrite,
    .fclose = null_fclose,
};    
And viola, you have a unit test. Now you have your knife in the seam you can push it in a bit further. For example, you can do a little spying:
/* legacy.test.c: Part 1 */
#include "stdio.seam.h"
#include <assert.h>
#include <string.h>

static FILE * null_fopen(const char * restrict filename, 
                         const char * restrict mode)
{
    return 0;
}
    
static size_t spy_fwrite(const void * restrict ptr, 
                         size_t size, 
                         size_t nelem, 
                         FILE * restrict stream)
{
    assert(strmp("2:9:18", ptr) == 0);
    return 0;
}

static int null_fclose(FILE * stream)
{
    return 0;
}

const struct stdio_t stdio =
{
    .fopen  = null_fopen,
    .fwrite =  spy_fwrite,
    .fclose = null_fclose,
};
This approach is pretty brutal, but it might just allow you to create an initial seam which you can then gradually prise open. If nothing else it allows you to create characterisation tests to familiarize yourself with legacy code.

You'll also need to create a trivial implementation of stdio.seam.h that the real code uses:
/* stdio.seam.c */
#include "stdio.seam.h"
#include <stdio.h>

const struct stdio_t stdio =
{
    .fopen  = fopen,
    .fwrite = fwrite,
    .fclose = fclose,
};
The -include compiler option might also prove useful.

-include file
    Process file as if #include "file" appeared as the first line of the primary source file.


Using this you can create the following file:
/* include.seam.h */
#ifndef INCLUDE_SEAM
#define INCLUDE_SEAM

#if defined(UNIT_TEST)
#  define LOCAL(header) "nothing.h"
#  define SYSTEM(header) "nothing.h"
#else
#  define LOCAL(header) #header
#  define SYSTEM(header) <header>
#endif

#endif
and then compile with the -include include.seam.h option.

This allows your legacy.c file to look like this:
#include LOCAL(wibble.h) 
#include LOCAL(stdio.seam.h)
...
int legacy(int a, int b)
{
    FILE * stream = stdio.fopen("some_file.txt", "w");
    char buffer[256];
    int result = sprintf(buffer, "%d:%d:%d", a, b, a*b);
    stdio.fwrite(buffer, 1, sizeof buffer, stream);
    stdio.fclose(stream);
    return result;
}    


10,000 warnings

Suppose you are working on a codebase that has 10,000 warnings. Perhaps you have discussed how you can get rid of the warnings. Perhaps you have discussed it more than once. But you still have 10,000 warnings. What can you do? There is no magic bullet. You are not going to be able to buy a magic tool, or wave a magic wand, and with almost no effort, get rid of them all. If there was you would already have waved the wand.

Even if there was such a silver bullet it would create a shallow improvement, rather than a deep one. It would create no dynamic whatsoever to encourage the developers to learn how not to introduce warnings in the first place. It would do the opposite.

Naturally new warnings keep appearing. Tomorrow there will be 10,001 warnings. But the 1 new warning gets lost in the sea of 10,000 others. It's not even noticed. The number of warnings is trending relentlessly upwards.

The only way you can get rid of 10,000 warnings is the same way you got them in the first place. A little bit at a time. With effort. Effort that shows you care.

The first thing to do is put a finger in the hole. Cap the number of warnings. Make new warnings count as errors from now on. Don't just say "new warnings count as errors from now on". You said that last time and it didn't work. Alter the way you build your software so that new warnings cause a build failure. If module X has 3,663 warnings then 3,663 is the stake in the ground. If it gets more than 3,663 make that a build failure.

This creates pressure to remove warnings on the code actually being worked on. In a culture where warnings are ignored to the extent that they've grown to be 10,000 strong, developers are not likely to see the merits of removing warnings from old code that seems to work and no one has touched for a long time.

Once the number of warnings has stabilized, or even started to trend slightly downwards, you can work on reducing the number of warnings. If the maximum number of warnings in the build is currently 10,000 then set a target. Agree that in a weeks time the number will have dropped to 9,900. Or agree to look at the classes of warnings and target the worst one.

You don't have to get to zero. Getting to zero would mean removing warnings from old code that hasn't been touched for ages. That's not as important as changing the dynamics of the how people act so that the number of warnings is going down rather than going up.

Once you get to a level your happy with, don't turn the build-checks off. Don't take your finger out of the hole. If you do that warnings can easily start to rise again. Leave them in place.

Once you get to a level your happy with, look at ways you can improve further. Buy new tools that detect new warnings. And start again. Focus on improvement not perfection.

Lean on the Compiler by Hiding (C++)

My Lean on the Java Compiler by Hiding post has proved quite popular so I've been thinking about if it could also work for C++... It can. I'll use Singleton as a example again. Suppose you have a C++ class like this:



class legacy
{
public:
    legacy();
    void eg1();
    void eg2();
    ...
};
#include "legacy.hpp"
#include "singleton.hpp"

void legacy::eg1()
{
    singleton::instance()->call();
}

void legacy::eg2()
{
    singleton::instance()->call();
}

Again, you want to create a seam for singleton access. The first step is to introduce the hiding identifier in the header file:

class legacy
{
public:
    legacy();
    void eg1();
    void eg2();
    ...
private:
    class singleton;  // <-----
};


Now Lean on the Compiler. All the calls to singleton::instance() no longer compile because the global singleton class is now hidden by the local singleton class just introduced. Next, in the source file add a global variable called instance of type singleton* and refactor all occurrences of singleton::instance() to instance.
#include "legacy.hpp"
#include "singleton.hpp"

singleton * instance;

void legacy::eg1()
{
    instance->call();
}

void legacy::eg2()
{
    instance->call();
}

Then Lean on the Compiler again to make sure there are no typos. Next remove the global variable called instance from the source file and refactor the header file to this:

class singleton; // <-----

class legacy
{
public:
    legacy();
    void eg1();
    void eg2();
    ...
private:
    singleton * instance; // <-----
};

Finally, initialize the instance member in the constructor definition.

legacy::legacy()
    : instance(singleton::instance())
{
}

I haven't tried this on real legacy code either. Caveat emptor!

Lean on the Compiler by Hiding (Java)

Here's a small trick which might not be out of place in Michael Feather's excellent book. I'll use Singleton as a example. Suppose you have a class like this (Java):



public class Legacy
{
    public void eg1()
    {
        stuff(Singleton.getInstance().call());
    }
    public void eg2()
    {
        more_stuff(Singleton.getInstance().call());
    }
}

And you want to create a seam for the singleton access. The first step is to introduce two public fields:

public class Legacy
{
    public int Singleton;
    public Singleton instance;

    public void eg1()
    {
        stuff(Singleton.getInstance().call());
    }
    public void eg2()
    {
        more_stuff(Singleton.getInstance().call());
    }
}

Now Lean on the Compiler. All the calls to Singleton.getInstance() no longer compile because the Singleton class is now hidden by the int Singleton just introduced. Next refactor all occurences of Singleton.getInstance() to instance (the other identifier just introduced, you can pick any name for this one but it's type must be Singleton).

public class Legacy
{
    public int Singleton;
    public Singleton instance;

    public void eg1()
    {
        stuff(instance.call());
    }
    public void eg2()
    {
        more_stuff(instance.call());
    }
}

Then Lean on the Compiler again to make sure there's no typos. Finally refactor to this:

public class Legacy
{
    public Singleton instance = Singleton.getInstance();

    public void eg1()
    {
        stuff(instance.call());
    }
    public void eg2()
    {
        more_stuff(instance.call());
    }
}

The name lookup rules for Java allow this to work but I don't think this idea will work in C++ (for example). I haven't got time to try it now as I'm heading off to the NDC conference in Oslo. I only thought of it yesterday. I haven't tried this on real legacy code. Caveat emptor!

Update. I have a C++ version aswell.

Adapt Parameter and Preserve Signature

Working Effectively With Legacy Code by Michael Feathers (isbn 0-13-117705-2) is a great book. The very first dependency breaking technique (p326) is called Adapt Parameter. It uses this Java example:



public class ARMDispatcher
{
    public void populate(ParameterSource request) {
        String[] values
            = request.getParameters(pageStateName);
        if (values != null && values.length > 0)
        {
            marketBindings.put( 
                pageStateName + getDateStamp(),
                values[0]);
        }
        ...        
    }
    ...
}


Michael writes:

In this class, the populate method accepts an HttpServletRequest as a parameter. ... It would be great to use Extract Interface (362) to make a narrower interface that supplies only the methods we need, but we can't extract an interface from another interface. ...


and a bit later...

Adapt Parameter is one case in which we don't Preserve Signatures (312). Use extra care.


Well, here's a variation on Adapt Parameter where we do Preserve Signatures. I'll stick with the Java example, but the idea is broadly applicable...

First we create our ParameterSource interface and fill it with the signatures of the methods in HttpServletRequest that our populate method calls:

public interface ParameterSource
{
    String[] getParameters(String name);
}
then we implement the adapter for HttpServletRequest:
public class HttpServletRequestParameterSource 
    implements ParameterSource
{
    public HttpServletRequestParameterSource(HttpServletRequest request) {
        this.request = request;
    }

    public String[] getParameters(String name) {
        return request.getParameters(name);
    }

    private HttpServletRequest request;
}
Next we add an overload of populate taking a ParameterSource and implement it with an exact copy-paste:
public class ARMDispatcher
{
    public void populate(ParameterSource request) {
        String[] values
            = request.getParameters(pageStateName);
        if (values != null && values.length > 0)
        {
            marketBindings.put(
                pageStateName + getDateStamp(),
                values[0]);
        }
        ...        
    }

    public void populate(HttpServletRequest request) {
        String[] values
            = request.getParameters(pageStateName);
        if (values != null && values.length > 0)
        {
            marketBindings.put(
                pageStateName + getDateStamp(),
                values[0]);
        }
        ...        
    }
    ...
}
Now we Lean on the Compiler (315) to make sure we haven't missed any methods in HttpServletRequest that our populate method calls. Add any we missed to the ParameterSource interface. Implement them in HttpServletRequestParameterSource as one liners. When it compiles we can refactor to this:
public class ARMDispatcher
{
    public void populate(ParameterSource request) {
        String[] values
            = request.getParameters(pageStateName);
        if (values != null && values.length > 0)
        {
            marketBindings.put(
                pageStateName + getDateStamp(),
                values[0]);
        }
        ...        
    }

    public void populate(HttpServletRequest request) {
        populate(new HttpServletRequestParameterSource(request));
    }
   ...
}
And now we have a seam we can pick it at...
public class FakeParameterSource 
    implements ParameterSource
{
    public String[] values;

    public String[] getParameters(String name) {
        return values;
    }
}