The Basic Approach


Lists involve at least two issues. We need to insert the starting XML, which will look like this:

 <UL> 
<LI></LI>
</UL>

Then, every time the user types an Enter while inside the <LI> tags, we want to generate another pair of tags and set the cursor inside, like this:

 <UL> 
<LI>I was just typing in here, and hit enter here.</LI>
<LI>I got this item. One more enter, to get this:</LI>
<LI></LI>
</UL>

The tag insertion should be easy. We ll just copy what we did for the <section> tag. Getting the <LI> elements to generate will be a bit more difficult: we ll have to make the code that generates new <P> elements be smarter .

We ll do one thing at a time. First we ll get the initial list structure in place, and then we ll work on generating the <LI> items. Let s begin by reviewing the current code. In XMLNotepad.cs, we have the menu- related code. These are the declarations:

 class XMLNotepad : Form { 
public TestableTextBox textbox;
private TextModel model;
private MenuItem insertPre;
private MenuItem insertSection;
private MenuItem openFile;
private MenuItem saveFile;
private ModelAction enterAction;
private ModelAction shiftEnterAction;
private ModelAction insertSectionAction;
private ModelAction insertPreTagAction;
private ModelAction saveAction;
private ModelAction loadAction;
private String fileName;
private Boolean displayDialog = true;
public delegate void ModelAction();
...

The MenuItem variables hold the actual menu items for the operation. As we ll see in a moment, these are looked up by their accelerators to trigger the insertions. The ModelAction delegate is used to define the specific action to be taken for each item. Let s trace the insertSection code, since it s a lot like what we ll need. Here s the relevant code from the Form s creation and initialization:

 class XMLNotepad : Form { 
public TestableTextBox textbox;
private TextModel model;
private MenuItem insertPre;
private MenuItem insertSection;
private MenuItem openFile;
private MenuItem saveFile;
private ModelAction enterAction;
private ModelAction shiftEnterAction;
private ModelAction insertSectionAction;
private ModelAction insertPreTagAction;
private ModelAction saveAction;
private ModelAction loadAction;
private String fileName;
private Boolean displayDialog = true;
public delegate void ModelAction();
public XMLNotepad() {
initialize(new TextModel());
} public XMLNotepad(TextModel model) {
initialize(model);
}
private void initialize(TextModel model) {
InitializeDelegates(model);
this.model = model;
this.Text = "XML Notepad";
... (File menu not shown)
insertSection = new MenuItem (
"Insert & Section",
new EventHandler(MenuInsertSection));
insertPre = new MenuItem (
"Insert &Pre",
new EventHandler(MenuInsertPre));
this.Menu = new MainMenu(new MenuItem[] {fileMenu, insertPre,
insertSection } );
this.textbox = new TestableTextBox();
...

What s important here is that we are creating the menu item and saving it. We ll of course need to create other menu items for our list elements. Let s remember to look for duplication in the menu code ”we can already see some ”and see if we can remove it. But for now we ll just make a note of that. Browsing on, let s look at how MenuInsertSection and the related code work:

 void MenuInsertSection(object obj, EventArgs ea) { 
CallModel(insertSectionAction);
}

private void CallModel(ModelAction modelAction) {
GetText();
modelAction();
PutText(textbox, model.LinesArray(), model.SelectionStart);
}

sprivate void InitializeDelegates(TextModel model) {
enterAction = new ModelAction(model.Enter);
shiftEnterAction = new ModelAction(model.InsertReturn);
insertSectionAction = new ModelAction(model.InsertSectionTags);
insertPreTagAction = new ModelAction(model.InsertPreTag);
saveAction = new ModelAction(this.SaveFile);
loadAction = new ModelAction(this.LoadFile);
}

This is a good refresher on how the delegates work. The menu click handler, MenuInsertSection, uses CallModel. CallModel takes care of updating the model with GetText(), performs the model action called for by its parameter, and then updates the TextBox with PutText().

We see that the model action for insert section is a method on the TextModel, InsertSectionTags(). The code in TextModel looks like this:

 class TextModel { 
private static string[] newParagraph = { "<P></P>" };
private static string paragraphSkip = "<P>";
private static string[] newSection = {" < sect1 >< title >< /title > "," < /sect1 > " };
private static string sectionSkip = " < sect1 >< title > ";
private static string[] newPre = { "<pre></pre>" };
private static string preSkip = "<pre>";
private static string[] emptyLine = { "" };
private static string emptyLineSkip = "";
private static object[] noArgs = {};
private ArrayList lines;
private int selectionStart;

public void InsertSectionTags() {
InsertTags(newSection, sectionSkip);
}

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

Here, the newSection string shows everything that needs to be inserted, and the sectionSkip string is the part to be skipped over. That s used to calculate the new cursor position. We should be able to add our menu item just like the others, almost by a copy and paste kind of process. We have a plan, so let s go forward.

start sidebar
Lesson: A Little Reflection

Whenever we start a new task, we review the code to get our bearings. There s nothing special about this: you probably do it yourself. It is, of course, possible to do this review from memory, but I find it useful to do it by looking explicitly at the code, for three reasons. First, my memory is fallible. Second, in a team project with team code ownership, the code might be different from the way it looked when I last saw it. Third...third...I forgot what I was going to say. I told you my memory was fallible. It s best to review the code.

As we put our plan together, we re speculating about what we will do. This is a plan, not a commitment. Our actual experience will guide us within this overall scheme. We ll try to do as little speculation in the code as possible: we ll put in what we need and nothing more. But we re always looking ahead in our mind, trying to see what is coming up.

To me, this process is a little like planning a short trip in the car. I plan to drive from my house out to Shehan Road, hang a left, go over to McGregor. There I ll turn right, up to M-36, then left to the drug store. I might vaguely reflect that if Shehan is blocked to the left, I ll go right and around Whitewood and up to M-36 that way. If McGregor right is blocked, I ll go left and over to Dexter-Pinckney Road and up. Mostly, I think I know what I ll do. If as I drive down Shehan, there s a kid on a bike, I ll slow way down. But I don t go super slowly if there is clear visibility: the limit on Shehan is 45. That car coming the other way might decide to pass the guy in front of him. I don t pull off the road because it might happen, but I m ready in case it does happen.

The same is true with our programming plans. We have a general idea of where we will go, and we plan to stay alert and to modify that plan as things happen. Too much planning wastes time. Too little and we might go off in the wrong direction. How much planning you need depends on your own level of comfort . My own view is that a delicate sense of whether we re going in the right direction reduces the need for planning, and it s useful as well during the course of our work. Suppose we were great at planning but had no sense of direction. The first thing that goes wrong ”and we all know something will go wrong ”will completely throw us. As soon as we get off course, we re doomed. Suppose we were bad at planning but had a perfect sense of direction. We might get off course, but our knowledge of local roads and our certainty about the general direction of the area would let us adapt quickly to changing situations.

When we plan our trip to the drug store, we don t think much. We just know what we re going to do, and we trust our ability to adapt to get us there. In our code, we re trying to do the same thing. A quick review, some direction setting, and we re off.

end sidebar
 
start sidebar
Lesson: Copy and Paste?

One last thing, and an important one. We are planning to implement our new feature by copying and modifying the code that s already there. Isn t copy and paste programming the lowest form of software development? Doesn t it lead to code that contains duplication, lacks modularity, and causes ridicule to be heaped on our heads? Well, yes. Shouldn t we figure out how to build these new features without copy and paste? Shouldn t we figure out some general way to do this, something table-driven, or something that keeps all the menus in a database or uses Web Services to find out what to do? Well, no, I don t think so.

As I ve mentioned, Extreme Programming folks have a rule called YAGNI: You Aren t Gonna Need It. When tempted to put in some code more general than what we have and what we need right now, we often say, We re going to need this later; we might as well do it now. The YAGNI rule reminds us that maybe we re going to need it, but maybe we aren t. It expresses that idea in the typical extreme XP way: directly and with certainty. So the YAGNI principle tells us to wait.

Andy Hunt and Dave Thomas, the Pragmatic Programmers, give us lots of advice in their book, and in their columns , about techniques that are useful, and when to use them. Concerned that the YAGNI principle can be overused , in one of their columns they offer a new principle, DOGBITE: Do it Or Get Bitten In The End. The idea they offer is that if we put off generalizing too long, we ll wind up with bad code, code that is hard to modify and that will slow us down in the long run.

Andy and Dave are correct: If we do not generalize soon enough, we will get in trouble. And YAGNI is correct: If we generalize too soon, we will make mistakes and waste time. How do we decide?

In my own practice, I choose to decide always in favor of YAGNI. However, I try to be very sensitive to code smells such as duplication, and I have a pretty good sense of direction, owing to my years of experience. So YAGNI works for me, in part because I tend to start in a decent direction, and I m ready to notice the signs that things are going wrong. In this book, I m trying to follow YAGNI exclusively, never putting in anything until I really need it. Part of the experiment in the book is to see whether this gets us in trouble.

In the case of our current stories, it s easy to see that we re going to get duplication, because the features are so much alike. I m going to let the duplication occur if it wants to and then remove it. I believe that if I speculate about what is needed, my guess will be wrong and waste time. If I look at the duplication that I create, it should become more clear exactly what, if anything, needs to be done. Let s watch and see what happens.

end sidebar
 



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