We move forward incrementally, improving the report generation by adding new paragraphs, observing missing ideas, and removing duplication. Could that be all there is to this? Is "all" a good word in that question?

We finished writing our test from last time, to drive out further capability in the report section of the program:

   @Test
    public void summaryPageHasGunInformation() {
        report.process("orlich");
        ReportPage summaryPage = report.pageNamed("SummaryPage");
        ReportParagraph gunPara = summaryPage.paragraphNamed("GunInformation");
        assertTrue(gunPara.text().contains("Ljutic Mono Gun"));
    }

(As is well known, Dan Orlich has Serial Number 1 of the Ljutic Mono Gun. Thus his report should reflect that.) Chet also changed the customer.txt file to look like this:

Dan Orlich
Ljutic Mono Gun

Clearly this file structure can’t last. But it’ll do for now. Now we’ll just make the program work. First, we extend PatternReport:

public class PatternReport {
    HashMap<String, ReportPage> pageMap = new HashMap<String, ReportPage>();

    public HashMap pages() {
        return pageMap;
    }

    public ReportPage pageNamed(String pageName) {
        return pageMap.get(pageName);
    }

    public void process(String folderName) {
        ClientInfo info = new ClientInfo(folderName);
        createCoverSheet(info);
        createSummaryPage(info);
        // other methods TBD
    }

    private void createCoverSheet(ClientInfo info) {
        ReportPage coverSheet = ReportPage.makeCoverSheet(info);
        pageMap.put("CoverSheet", coverSheet);
    }   

    private void createSummaryPage(ClientInfo info) {
        ReportPage summaryPage = ReportPage.makeSummaryPage(info);
        pageMap.put("SummaryPage", summaryPage);
    }
}

Next, we add the feature to ReportPage:

public class ReportPage {
    HashMap<String, ReportParagraph> paragraphMap = new HashMap<String, ReportParagraph>();

    public ReportParagraph paragraphNamed(String paraName) {
        return paragraphMap.get(paraName);
    }

    public static ReportPage makeCoverSheet(ClientInfo info) {
        ReportPage coverSheet = new ReportPage();
        coverSheet.fillInCoverInfo(info);
        return coverSheet;
    }

    private void fillInCoverInfo(ClientInfo info) {
        ReportParagraph namePara = new ReportParagraph();
        namePara.setClientName(info);
        paragraphMap.put("ClientName", namePara);
    }

    public static ReportPage makeSummaryPage(ClientInfo info) {
        ReportPage summaryPage = new ReportPage();
        summaryPage.fillInSummaryInfo(info);
        return summaryPage;
    }

    private void fillInSummaryInfo(ClientInfo info) {
        ReportParagraph gunPara = new ReportParagraph();
        gunPara.setGunName(info);
        paragraphMap.put("GunInformation", gunPara);
    }
}

The fillInSummary method is called for by the code in the makeSummaryPage method. Now we need to improve ReportParagraph:

public class ReportParagraph {
    String text = "uninitialized";

    public String text() {
        return text;
    }

    public void setClientName(ClientInfo info) {
        text = info.clientName();
    }

    public void setGunName(ClientInfo info) {
        text = "This report refers to your gun, the " + info.gunName() + ".";
    }
}

We asked the customer to give us a sample paragraph text, so that we could show that the gun name was correctly inserted into the text. (Our test doesn’t completely test that, but we are confident that it works. Maybe we’ll write another test in a minute.) Now we have to update ClientInfo:

public class ClientInfo {
    final String folder = "Data\\ShotgunPatternData\\";
    private String clientName;
    private String gunName;

    public ClientInfo(String folderName) {
        String fileName = folder + folderName +  "\\customer.txt";
        try {
            BufferedReader in = new BufferedReader(new FileReader(fileName));
            clientName = in.readLine();
            gunName = in.readLine();
            in.close();
        } catch (IOException e) {
        }
    }

    public String clientName() {
        return clientName;
    }

    public String gunName() {
        return gunName;
    }
}

These changes go right in, prompted almost completely by Eclipse. No brain power required. What’s not to like?

What's Not To Like???

To add one new paragraph, we had to change four classes: PatternReport, ReportPage, ReportParagraph, and ClientInfo. Each change was simple but this one concept, adding a new paragraph, is spread over four classes. We are required by Beck’s “Rules for Simple Code” to express our ideas clearly. The code reflects the code structure, but doesn’t express very well the notion of “Hey, just add a new paragraph on such and such a page.

Plus, there is quite a bit of duplication. All those new methods look just like methods that already exist. In fact, we cut and pasted most of them. (Eclipse should have a warning message when you do that. But we noticed anyway.)

Our test runs, but our code is not yet done, because it doesn’t express our design ideas, and it contains duplication. We need to improve it.

Quick Design Session

Chet and I took five, maybe ten minutes to talk about what to do to refactor this code to be better. We were predicting what the design would turn out to be, mostly to give ourselves a little comfort with what is going to happen, and to set our direction a little bit. We want to be comfortable that we don’t have to rewrite, rebuild, redesign, or anything major. If we will need to do those things, we want to know it soon. Here’s roughly what we think will happen.

We see the duplication at the bottom, where two methods set the text of two paragraphs. We’ll extract a method that substitutes a ClientInfo string into a paragraph string. That will leave us with two calls to that method, and those two calls will look a lot alike. So we’ll remove the duplication, probably by pushing the variability up the tower of Report, Page, Para. As we do that, we will need to replace the explicit calls to methods with data values.

That will require us, at some point, to add key information to the customer.txt file, something like clientName:Dan Orlich and so on. We’ll push the code all the way up until the PatternReport process method looks like a series of calls with nothing but text strings, and the ClientInfo object, in each call.

That will suggest that the strings should be pulled out into some kind of an object list we can loop over. And to get the strings, we’ll want to put them all into some kind of file structure.

That’s all speculation, but it gives us a sense of what might happen, an dit makes us very comfortable that everything is going to be ok. We’re sure enough to go ahead with pure refactoring, not by constructing a bunch of things that we might need. We are not committed, in any way, to this sequence of events, or this outcome. The code will tell us what to do, as we refactor it.

Refactoring

We begin with a look at this:

   public void setClientName(ClientInfo info) {
        text = info.clientName();
    }

    public void setGunName(ClientInfo info) {
        text = "This report refers to your gun, the " + info.gunName() + ".";
    }

Is the duplication here obvious? Something from info is being put into text, with some other stuff, maybe. Chet edited the two methods this way to make the duplication a bit more obvious:

   public void setClientName(ClientInfo info) {
        text = "" + info.clientName() + "";
    }

    public void setGunName(ClientInfo info) {
        text = "This report refers to your gun, the " + info.gunName() + ".";
    }

The tests are still green. Now we imagine that as time goes on, there will be more than one substitution and other complexities. We were thinking about a string substitution protocol with %fieldName% kinds of things in the string. But we certainly don’t need that now. We’re here to push incremental development, so for now, we’ll just provide a pre-string, a variable, and a post-string. As the code evolves, pay attention to whether there is a lot of rework involved in what seems to be a short-sighted decision. We predict that there won’t be much rework at all, just addition. So … let’s reduce duplication:

First, we rewrite as follows:

   public void setGunName(ClientInfo info) {
        String pre = "This report refers to your gun, the ";
        String post = ".";
        String variable = info.gunName();
        text = pre + variable + post;
    }

Having done that, we extract a setParagraphText method:

   public void setGunName(ClientInfo info) {
        String pre = "This report refers to your gun, the ";
        String post = ".";
        String variable = info.gunName();
        setParagraphText(pre, variable, post);
    }

    private void setParagraphText(String pre, String variable, String post) {
        text = pre + variable + post;
    }

We change the other method to use setParagraphText:

   public void setClientName(ClientInfo info) {        
        setParagraphText("", info.clientName(), "");
    }

The tests remain green. Now, to make the duplication more obvious, let’s inline those temps, with this result:

public class ReportParagraph {
    String text = "uninitialized";

    public String text() {
        return text;
    }

    public void setClientName(ClientInfo info) {        
        setParagraphText("", info.clientName(), "");
    }

    public void setGunName(ClientInfo info) {
        setParagraphText("This report refers to your gun, the ", info.gunName(), ".");
    }

    private void setParagraphText(String pre, String variable, String post) {
        text = pre + variable + post;
    }   
}

Now we see some more duplication. Here are two “duplicate” methods, with their variability highlighted:

   public void setClientName(ClientInfo info) {        
        setParagraphText("", info.clientName(), "");
    }

    public void setGunName(ClientInfo info) {
        setParagraphText("This report refers to your gun, the ", info.gunName(), ".");
    }

Now our mission is to remove that duplication. We talk briefly about how we might do this. We sketch this method in ReportParagraph:

   public void setText(String pre, String variableKey, String post, ClientInfo info) {
        String variable = info.lookup(variableKey);
        setParagraphText(pre, variable, post);
    }

The idea is that we can call this method from ReportPage, providing all the text and variable info for each ReportParagraph, if we implement the ability of the ClientInfo to look up values instead of find them by method. That will parameterize clientInfo, force an enhancement to the customer.txt file, and push the report text higher up in the system, where it will probably centralize enough to be extracted as a file structure of some kind.

Or so we imagine. This is where we’ll stop for today and retrospect.

Retrospective

Seems OK so far. The objects are getting more general, abstractions are beginning to come out of the fog, and the tests are all green (except for the compile error for the setText method above, which we’re leaving in to remind us what to do tomorrow. As of now it seems clear that this is going to work just fine, more or less as we imagined.

It’s early in this particular thread of development, but so far there are no surprises and the results seem to be the same as what generally happens. When we work in very small steps, writing very simple implementations, and thenn go over the code removing duplication and adding clarity, we get these results:

  • Progress goes smoothly and quickly;
  • We rarely have deep defects;
  • We rarely need the debugger;
  • We move from "working" to "working better" on a very short cycle;
  • We don't ever seem to replace huge chunks of code;
  • When new implementations are needed, they are isolated, and they replace very simple existing implementations.

We work this way a lot. We put in implementations that seem brutally simple, almost stupid. We use them a time or two, and we get duplication. We do fairly obvious duplication removal, or sometimes we see that the code is asking for some object to be created. This usually shows up as an idea in our heads that appears in some strange way in the code. Instead of imagining some complex new object and creating it in anticipation of use, we just let it grow. We are making “business” progress all the time, in a more or less steady fashion. We have few delays for building in big frameworks of our own invention, or rewriting things. We have a near-constant velocity, and when we fall below that, we notice it immediately.

It seems to me that the more incrementally we work, the better things go. It seems to me that the code just tells us what to do next, with very clear signs, and usually very clear solutions.

One point of concern is this: are Chet and I the only people in the world who could do this? The duplication and its removal seem so obvious to us. We are doing very simple refactorings, moving things in directions that seem obvious. But is this apparent simplicity really the result of some genius-level thinking that is going on behind the scenes? It’s hard to know. I am certainly prepared to admit to having genius-level thinking going on. <smile/>

A related issue comes up from time to time, most recently from John Donaldson. Is this process somehow less efficient than just figuring out what you need (WAGNI) and doing it? It would seem that incremental might be less efficient, because after all we implement things and then next moment they are gone. Wouldn’t typing in the right thing have been more efficient?

Well, we don’t think so. We have brains the size of planets and all, but still we find that when we imagine what we “will” need, we wind up imagining all kinds of things that we don’t need, we take big bites that don’t work, and we sill wind up with an interface that only an architect could love. So we wait a long time to get actual work donw, and then have to rework our brilliant idea.

I’m not saying that’s true for you, though I believe that it likely is. What I’m saying is that when you work at this incredibly fine-grained level, you’re sure to learn something, even if it’s “Never go here again.” My bet is that you’ll learn you like it there … but for sure, you’ll learn.

Stay tuned. We’re back to programming tomorrow …