Javaв„ў EE 5 Tutorial, The (3rd Edition)

With the tests in place, it s time to use our next session to improve the code s ability to express our intention . At this point, the code looks like this:

public void InsertParagraphTag() { // // On Enter, we change the TextModel lines to insert, after the line // containing the cursor, a blank line, and a line with < P >< /P > . We set the // new cursor location to be between the P tags: < P > < /P > . // int cursorLine = LineContainingCursor(); // allocate a new line array with two more lines String[] newlines = new String[lines.Length+2]; // copy line 0 through cursor line to output for(int i = 0; i <= cursorLine; i++) { newlines[i] = lines[i]; } // insert blank line newlines[cursorLine+1] = ""; // insert P tag line newlines[cursorLine+2] = "<P></P>"; // copy lines after cursor lien to output for (int i = cursorLine+1; i < lines.Length; i++) { newlines[i+2] = lines[i]; } // save new lines as result lines = newlines; // set cursor location selectionStart = NewSelectionStart(cursorLine + 2); }

Lesson  

Do you find those comments to be helpful? Chet and I do not. They do tell a bit about the intention of the various bits of code, and that s good. But they also make the code seem choppy and hard to read. This code is a good example of Kent s suggestion that a comment is the code s way of telling us that it wants to be more clear.

To make this code better, we just kept pecking away at it, changing things around to make the code more expressive and, in some cases, to use approaches that seemed better to us in C#. The first of these ideas was the best. This version uses two arrays of strings, creating a new array, newlines , to be two more lines than the existing lines array. We knew there was an object in C#, ArrayList, which is basically an array that can grow, have insertions, and so on. So our first step was to change the object to use two ArrayLists instead of two arrays. To do this, we needed to change the Lines property:

public String[] Lines { get { // there must be a way to cast this?? String[] textlines = new String[newlines.Count]; for (int i = 0; i < newlines.Count; i++) { textlines[i] = (String) newlines[i]; } return textlines; } set { lines = new ArrayList(value); newlines = lines; } }

There s nothing magical about this. The interface we chose expects arrays of string, so we re converting to and from ArrayList internally. There might be a better way to do the get operation, but at the Michigan Union, with no C# books, we sure couldn t find it. So we create an array whose size matches newlines.Count. (For reasons known only to Microsoft, ArrayList uses Count where Array uses Length.) Then we copy the newlines ArrayList into the array. We re hoping to find a way to just cast the ArrayList to an array of String, and when we do, we ll change this code to be more clean.

We also had to change the TextModel constructor to initialize the lines and newlines variables to ArrayLists. (I think we had actually written the constructor during the testing phase described previously, when we built the test of an empty TextModel.)

public TextModel() { lines = new ArrayList(); newlines = lines; }

It also turns out that the InsertParagraphTag method includes some special case code, handling the empty case. I ll just show it here for completeness. We re hoping that when the refactoring is done, it will fall into the general case, but for now it s separate:

// handle empty array special case (yucch) if ( newlines.Count == 0 ) { newlines.Add( "<P></P>" ); selectionStart = 3; return; }

Slowly, things are getting better. We are now really set up to do some better work. ArrayList has a method, AddRange, that lets you add a section of one ArrayList into another, so we can rewrite all that array copying this way:

newlines = new ArrayList(); newlines.AddRange(LinesThroughCursor()); newlines.AddRange(NewParagraph()); newlines.AddRange(LinesAfterCursor());

Note that we extracted two methods , LinesThroughCursor and LinesAfterCursor. They look like this:

public ArrayList LinesThroughCursor() { return lines.GetRange(0,LineContainingCursor()+1); } public ArrayList LinesAfterCursor() { int cursorLine = LineContainingCursor(); return lines.GetRange(cursorLine+1,lines.Count - cursorLine - 1); }

And we wrote a NewParagraph method to answer the blank line and paragraph tag pair:

public ArrayList NewParagraph() { ArrayList temp = new ArrayList(); temp.Add(""); temp.Add("<P></P>"); return temp; }

The net result of all this is to replace this code:

// allocate a new line array with two more lines String[] newlines = new String[lines.Length+2]; // copy line 0 through cursor line to output for(int i = 0; i <= cursorLine; i++) { newlines[i] = lines[i]; } // insert blank line newlines[cursorLine+1] = ""; // insert P tag line newlines[cursorLine+2] = "<P></P>"; // copy lines after cursor lien to output for (int i = cursorLine+1; i < lines.Length; i++) { newlines[i+2] = lines[i]; }

with this:

newlines = new ArrayList(); newlines.AddRange(LinesThroughCursor()); newlines.AddRange(NewParagraph()); newlines.AddRange(LinesAfterCursor());

Категории