Lessons Learned


Lesson  

Well, first of all, I m a good programmer, or at least not a bad one. Even with all this hassle, I got the feature working in only a few tries , and I never went deeply down any rat holes. So I don t have to kill myself . That s good, because I m sure you are wondering how all this turns out.

Second, I really do work better with a pair. There s no doubt in my mind that any reasonable pair would have seen some of these problems, questioned others, advised simpler tests, and generally made things go more smoothly. I wonder if Chet would consider quitting his job.

Third, programming by intention didn t work as well as it might have. The code was basically correct, but there were a surprising number of off-by-one errors, and the big test didn t show up precisely where those were. That surprised me: programming by intention is one of my favorite techniques. Let s come back to that.

Fourth, although getting into the debugger might have been good, writing the simpler tests was certainly good: it pointed directly to the mistakes, one after another, and the tests will serve as good documentation for later.

Fifth, even with the first small test helping me, I moved quickly to trying to make the big test work, only updating the small one when the big one failed yet again. This may be the most important lesson for me: proceed in smaller, test-driven steps, especially when working alone.

Finally, the off-by-one errors concern me a bit. For this feature, at least, they make me wonder whether the TextModel is quite right for the task. It seems that this particular code was a little bit primitive and not as expressive as we might like. I don t have an answer for that, but I m putting it on the list of things to stay alert for.

Programming by Intention

Let s look at that code again. It seems pretty simple and nearly good, but something went wrong:

 public void InsertReturn() { 
string front = FrontOfCursorLine();
string back = BackOfCursorLine();
lines[LineContainingCursor()] = front;
lines.Insert(LineContainingCursor()+1, back);
selectionStart += Environment.NewLine.Length;
}

Now that code was in fact correct ”so far so good. The FrontOfCursorLine method was also correct, but BackOfCursorLine wasn t:

 public string FrontOfCursorLine() { 
string line = (string) lines[LineContainingCursor()];
int position = PositionOfCursorInLine();
return line.Substring(0, position);
}
public string BackOfCursorLine() {
string line = (string) lines[LineContainingCursor()];
int position = PositionOfCursorInLine();
return line.Substring(position);
}

BackOfCursorLine referred to position+1 originally, but position was correct. My tests found the problem, and once I wrote small enough tests, pointed right to it. Maybe if I had drawn a little picture and counted the characters I would have stayed out of trouble. But then there was another, prior, problem in PositionOfCursorInLine:

 public int PositionOfCursorInLine() { 
return selectionStart - SumLineLengths(LineContainingCursor());
}

That used to say LineContainingCursor()-1 . I missed the fact that SumLineLengths goes up to the line in question, not through it. That method name isn t helping us. What does that method do? What s a better name : SumLineLengthsUpTo? TotalLineLengthsBeforeLine? NumberOfCharactersBefore? Let s see how else the method is used. It shows up here:

 private void InsertTags(string[] tagsToInsert, string tagsPrecedingCursor) { 
int cursorLine = LineContainingCursor();
lines.InsertRange(cursorLine+1, tagsToInsert);
selectionStart = NewSelectionStart(cursorLine + 1, tagsPrecedingCursor);
}
private int NewSelectionStart(int cursorLine, string tags) {
return SumLineLengths(cursorLine) + tags.Length;
}

Here, again, what we mean it to be is the total size of all the lines up to but not including cursorLine. It definitely needs a better name. NumberOfCharactersUpToLine? CharacterPositionOfLine? Now that is starting to work. The value we have is the cursor position of the beginning of the line indicated. CursorPositionAtFrontOfLine? CursorPositionOfLine? StartPositionOfLine? FirstPositionOfLine?

Lesson  

You re perhaps wondering if it s worth it to rename this method. After all, it s used only twice. On the other hand, it has confused me at least once. One out of two is a pretty bad average for understanding the name. SumLineLengths is how it is computed, but it isn t what it means . We need to name things by what they mean, not how they are implemented. I think I like FirstPositionOfLine. I ll change it and read the code again:

 public int PositionOfCursorInLine() { 
return selectionStart - FirstPositionOfLine(LineContainingCursor());
}
private int NewSelectionStart(int cursorLine, string tags) {
return FirstPositionOfLine(cursorLine) + tags.Length;
}
private int FirstPositionOfLine(int cursorLine) {
int length = 0;
for (int i = 0; i < cursorLine; i++)
length += ((String)lines[i]).Length + Environment.NewLine.Length;
return length;
}

Yes, I like that better. It might not be the whole cause of the off-by-one errors, but it certainly contributed to one. And it really is an issue with programming by intention, because this method s former name, SumLineLengths, doesn t express our intention as well as the new name, FirstPositionOfLine.

Lesson  

Some programmers, and some managers, resist efforts to give things the right names . In my opinion, this is very shortsighted. We can see from this example that reliability and maintainability can be significantly improved by careful attention to naming and to the meaning of the code.




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