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

We have another story that we agreed to do: Ordered List. Take a look back at the seven steps (in the What s the Opposite of Simple? section) we used to have to do to implement a new set of tags, and let s see what we have to do now. We ll begin with a Customer Acceptance Test:

*altO *output <OL> <LI></LI> </OL>

Simple enough. We need a new menu item, which wants to look like this:

insertOrderedList = new NotepadMenuItem ( "Insert & OL", new EventHandler(MenuInsertTags), TextModel.Tags.OrderedList ); this.Menu = new MainMenu(new MenuItem[] {fileMenu, insertPre, insertSection, insertUnorderedList, insertOrderedList } );

We must add the new variable:

private MenuItem insertPre; private MenuItem insertSection; private MenuItem insertUnorderedList; private MenuItem insertOrderedList;

With the previous implementation, we also had to add a new EventHandler method, add a new ModelAction variable, and initialize that variable to call a method inside the TextModel. We save all that effort, and perhaps more important, we don t have to remember to do it!

Our new code doesn t compile so far, with the message that TextModel.Tags doesn t include OrderedList. We build that and the support for it, as follows :

class TextModel { private ArrayList lines; private int selectionStart; private static string[] newParagraph = { "<P></P>" }; private static string[] paragraphSkip = { "<P>" }; private static string[] newListItem = { "<LI></LI>" }; private static string[] listItemSkip = { "<LI>" }; private static string[] emptyLine = { "" }; private static string[] emptyLineSkip = { "" }; private static string[] newSection = {"<sect1><title></title>","</sect1>" }; private static string[] sectionSkip = { "<sect1><title>" }; private static string[] newUnorderedList = {"<UL>","<LI></LI>","</UL>"}; private static string[] unorderedListSkip = { "<UL>", "<LI>" }; private static string[] newOrderedList = {" < OL > "," < LI >< /LI > "," < /OL > "}; private static string[] orderedListSkip = { " < OL > ", " < LI > " }; private static string[] newPre = { "<pre></pre>" }; private static string[] preSkip = { "<pre>" }; public enum Tags { Pre = 1, Section = 2, UnorderedList = 3, ListItem = 4, Paragraph = 5, OrderedList = 6 } private static string[] InsertStrings(Tags tag) { if (tag == Tags.Pre) return newPre; else if (tag == Tags.Section) return newSection; else if (tag == Tags.UnorderedList) return newUnorderedList; else if (tag == Tags.OrderedList) return newOrderedList; else if (tag == Tags.ListItem) return newListItem; else return newParagraph; } private static string[] SkipStrings(Tags tag) { if (tag == Tags.Pre) return preSkip; else if (tag == Tags.Section) return sectionSkip; else if (tag == Tags.UnorderedList) return unorderedListSkip; else if (tag == Tags.OrderedList) return orderedListSkip; else if (tag == Tags.ListItem) return listItemSkip; else return paragraphSkip; }

This almost works, but not quite: the customer test fails at this line:

private void ExecuteMenu(string accelerator) { MenuItem mi = form.MenuForAccelerator(accelerator); mi.PerformClick(); }

We ve still forgotten one thing (but only one). We haven t picked up the new menu here:

public MenuItem MenuForAccelerator(string accelerator) { if (accelerator == "&S") return insertSection; if (accelerator == "&P") return insertPre; if (accelerator == "&U") return insertUnorderedList; if (accelerator == " & O") return insertOrderedList; if (accelerator == "^O") return openFile; if (accelerator == "^S") return saveFile; return null; }

With this change, the tests run perfectly . We have added OrderedList in a couple of minutes!

Things still are not perfect. I m most troubled by the fact that I had to remember to add that menu to the MenuForAccelerator() method, and I m a little troubled by the fact that I had to change five lines in the TextModel, even though they were all adjacent and pretty obvious. But for sure, things are much improved.

Think about it. This is really a rather large design change, yet it went largely without difficulty, and in very little time.

Lesson: Incremental Design and Development

Up until now, the incremental changes we ve been making have been simple, and most of them have been localized. The delegate changes we made in Chapter 22, The Delegate from Troy, were a bit larger, but even they were done in a short evening at the Michigan Union. This time the changes were far more significant, involving three significant classes, the XMLNotepad, the TextModel, and the tests. We implemented a new class, NotepadMenuItem, a subclass of MenuItem. We implemented an enumeration to encapsulate the idea of a command between Form and Model and between tests and Model. We refactored all those classes extensively and simplified them significantly.

Most important, we did it in small steps, never more than a moment or two from running tests. If we needed to ship the code, we could have done so at any moment, because the tests were always running, even if the code improvement wasn t finished yet.

Could we have done a better job earlier? Could we have seen that we should build this structure way back at the beginning? Well, perhaps you could have. I myself could not, new as I was to C# and to this application. And, frankly, if we had invested in this design, I fear that we would have spent a lot of time on it and still probably have gone down some wrong paths. As it is, we shipped running, tested software to our customer, and we kept it running, tested , and improving as we went. We have reached a better point ”though not a perfect one ”where it is now much easier and more reliable to add new tags.

 

Lesson: Open Questions

There are still some open questions, of course. There always will be. The ones that I see include these:

 

Категории