Building SelectionStart


I wanted to go ahead and build SelectionStart, but Chet asked if I was going to write a programmer unit test first. I of course agreed.

Lesson  

This is a good habit to have, and especially in this new language we should try to practice good habits. In the larger scheme of things, customer tests might take longer to run. When this happens, we might tend to run customer tests less frequently than programmer tests. Furthermore, programmer tests generally point more directly to the problem when they fail. Therefore, when a customer test fails, the wise programmer writes a failing programmer test and then makes it work.

We added a line to our OneDirtyLine test, like this:

 [Test] public void OneDirtyLine() { 
command = new InputCommand(new StringReader("ab\n*end"));
AssertEquals("ab", command.CleanLines()[0]);
AssertEquals(1,command.SelectionStart());
}

This is enough to drive a trivial implementation of SelectionStart that returns 1, and then we added a line to our OneLineCommand test. We figured that because it didn t have a cursor indicator, the answer should be at the end of the lines.

 [Test] public void OneLineCommand() { 
String oneLineString =
@"one line
*end";
StringReader reader = new StringReader(oneLineString);
command = new InputCommand(reader);
AssertEquals(1, command.CleanLines().Count);
AssertEquals(10,command.SelectionStart());
}

That was enough to get us to write the following, with some help from similar code that s elsewhere in our little system:

 public int SelectionStart() { 
int charactersSoFar = 0;
foreach (String line in lines) {
int index = line.IndexOf("");
if (index != -1)
return charactersSoFar + index;
else
charactersSoFar += line.Length + Environment.NewLine.Length;
}
return charactersSoFar;
}

Now we really did type this in in one go. We had written a bunch of line- tracking loops recently, and this one just fell trippingly from my fingers. We keep track of how many characters we have read so far (which will be the number of characters in all the lines ahead of the line containing the vertical bar cursor indicator). When we find the first line with a vertical bar, we return the characters so far, plus the index of the cursor. That ll be the position in the whole file of the vertical bar. If the line doesn t contain the vertical bar, we update charactersSoFar.

Both our unit tests ran. However, most of the customer tests threw exceptions: index outside array. We fumbled with this a bit and then figured out how to tell from NUnit where the exception was thrown. We finally realized that when we run off the end of the list without finding the vertical bar, we set the selection start after the newline on the last line. Bad things happen because the TextModel thinks the cursor is in the next line after the last line. We realized that if SelectionStart finds no cursor, it should assume the cursor is in the last line, not after it. So we have to back out the last NewLine. The new SelectionStart is therefore

 public int SelectionStart() { 
int charactersSoFar = 0;
foreach (String line in lines) {
int index = line.IndexOf("");
if (index != -1)
return charactersSoFar + index;
else
charactersSoFar += line.Length + Environment.NewLine.Length;
}
return charactersSoFar - Environment.NewLine.Length;
}

And the tests all run! We think our story is actually running now and that all the customer tests imaginable for this feature will work. We ll probably write a few more, but for today, we re finished.

start sidebar
Lesson: What We Learned

Backing out our code earlier was pretty scary, but we learned that ”as usual ”it was a good idea. We guessed that we weren t using objects wisely, and our success with the InputCommand object confirms that. And from experience we learned that this approach is far less error-prone .

We learned that our reflexes to create new objects need to be more sensitive. A lot more work is required in C# (and Java) when building a new class, compared to what we re used to in Ruby and Smalltalk, but the need for new objects is just as strong, so we have to be more sensitive to that need.

Considering the time it takes to build it ”just a couple hours work ” the payoff for the new object is large: simple code, nicely partitioned off, easy to test and write. We certainly hope you can sense, both from our expression of the experience and from the code steps themselves , how much easier it was this time. It took only about half the original time, by the way, with far less stress.

Cruft Buildup

However, there s some cruft [2] buildup in the system now. A general confusion between arrays of strings and ArrayLists exists, although we cleaned some of this up as we went by virtue of having chosen ArrayList as the preferred object in InputCommand. Somewhere earlier, I didn t mention a couple of edits, where we matched up the interfaces. The TextModel used to take an array of strings in its Lines method and convert it to an ArrayList ”now it takes an ArrayList. And because the TextForm wants an array of Strings, we had to convert the ArrayList to strings in a new place. We didn t want to go into that, because it interrupted the flow of the narrative more than it interrupted the coding progress. Which brings me to my next point.

Precompile Checking for Watts Humphrey

I had dinner with Watts Humphrey at XP/Agile Universe. Watts is very much into desk checking code, and I said that we d try going over our code by eyeball before compiling, shooting for clean compiles. We haven t started doing that yet, but we did talk about it a few times as we went along. The standard XP thing to do is to type in a test, or some code, and then compile and run the tests. You let the compiler, or the failed test, direct your attention to the next thing. We were still doing that. We noticed that we would quite typically do something like the following:

We changed the return type of CleanLines from String[ ] to ArrayList. We could have looked through the code to see where the changes needed to be made. Instead, we compiled and then clicked on the compiler errors, which took us in the editor to the lines that needed changing. We fixed them and went on. Few if any of the compiler errors surprised us. And the very few logic problems we had, we believe, we would have detected by eyeball.

So we haven t done the experiment yet, but we remain committed to doing so. My subjective impression is that while in the olden days we might have printed a listing and looked at it, marking what we saw, the modern tools change the cost/benefit equation. To scrunch through the code looking for places that assume String[ ] would be time-consuming and subject to human error. To let the compiler do it is faster and completely accurate. The downside, of course, is that while scanning for those errors, we might well find other errors the compiler cannot help with. This time, we had very few such errors, and as far as we know, there are none that weren t detected by the tests. (I still have to look at that Replace method, though.)

Right now, our tentative conclusion is that the tools are different enough to move the best strategy in the direction of automation, especially in the presence of so many tests.

end sidebar
 

[2] Cruft is an unpleasant substance, the results of shoddy construction. Look it up in one of the jargon dictionaries on the Web for more information.




Extreme Programming Adventures in C#
Javaв„ў EE 5 Tutorial, The (3rd Edition)
ISBN: 735619492
EAN: 2147483647
Year: 2006
Pages: 291

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net