XProgramming > XP Magazine > Adventures in C#: Some Things We Ought to Do
COLLECTED TOPICS: Adventures in C# | Documentation in XP | Book Reviews
Adventures in C#: Some Things We Ought to Do
Ron Jeffries
01/20/2003
Over the weekend, I did a little studying. I was reading Charles Petzold's book,Programming Microsoft Windows with C#, and James Cooper's C# Design Patterns. They're both wonderful resources, and I got a lot of ideas. Then today, I took a break from most immediate things, and spent a little time at lunch writing ideas on cards about how the XML Notepad should be improved. Here are a few of those ideas, with a little explanation of each one, and some technical discussion.

Contents:

Command Pattern

The Command pattern is a natural for the Notepad. I spent some time thinking about building a subclass of MenuItem that would allow attachment of a Command object, which would then be used to dispatch the action to the TextModel. It should be possible to have all the menu items go to one method, which would init the TextModel, execute the command, and read the TextModel back into the TextBox. This would eliminate the duplication of the init and de-init that we're seeing now in code like:

    void MenuInsertSection(object obj, EventArgs ea) {
      model.SetLines(textbox.Lines);
      model.SelectionStart = textbox.SelectionStart;
      model.InsertSectionTags();
      PutText(textbox, model.LinesArray(), model.SelectionStart);
    }

Each of the menu items that we have now, and all the future menu items that update the model, have those lines passing stuff back and forth to between the model and the textbox. Use of the Command Pattern looks to me like a good way to do this.

Adapter Pattern

Another way to go on this, would be with the Adapter pattern. We sort of have Adapter in place now. Note the call to PutText on the textbox, up above. That's not a native method of TextBox class, but is a method of our specialized subclass of TextBox, TestableTextBox. This is called the "class adapter" approach. We could also build a separate object that handled the situation, containing a TextBox for which it does translation. That would be called "object adapter".

I've been complaining about the relationship between TextModel and the TextBox for a long time now, and maybe this is a good approach.

Mediator Pattern.

Right now, the Form understands the mapping between keyboard and menu operations, and specific operations of the TextModel. For example, we have the code:

      if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.None) {
        model.Enter();
        kea.Handled = true;
      }
      else if (kea.KeyCode == Keys.Enter && kea.Modifiers == Keys.Shift) {
        model.InsertReturn();
        kea.Handled = true;
      }

In the second of these cases, the Form knows that Shift-Enter means "Insert Return". In the first of the cases, the Model is told Enter(), and it contains the code to decide what to do:

    public void Enter() {
      InsertParagraphTag();
    }

I've been concerned about this for a while. There has been discussion of which way is right, and we have an interaction with the Customer Tests as well. We've talked about that before. The Mediator pattern would allow me to break out the translation from form operations like keystrokes and menu actions, into a separate class to encapsulate the idea. This could be really good.

Small Boy with a Patterns Book

After spending a bunch of time thinking about these ideas, over a few days now, I finally recognized in myself what I call "Small Boy with a Patterns Book". You can always tell when someone on your team is reading the Gang of Four book (Gamma, et al., Design Patterns). Every day or so, this person comes in with a great idea for a place in the system that is just crying out for the use of Composite, or whatever chapter he read last night.

There's an old saying: To a small boy with a hammer, everything looks like a nail. As programmers, we call into the same trap all too often. We learn about some new technology or solution, and we immediately begin seeing places to apply it.

Now I'm not saying that any of these pattern applications is wrong. At this moment, I'm just noting that I've gone into that mode. I'm looking for places in my code to use these really good ideas from Cooper's book.

Deriving a File Class

Cooper gave me another good idea. .NET has no decent File class. By "decent", I mean a class that represents a file, with a name, and a path, that can be used to open, close, rename, delete, and so on. Instead, there is this all-powerful "Directory" class that can do those things. What this means, however, is that when you want to do anything with files, you have nothing to work with but a collection of string representing the names of the files. String, of course, has no methods that are anything to do with files.

Cooper suggests building a class to support the kinds of things we need to do with files. Since this Directory to String thing has irritated me for quite a while, I'm thinking it's a good idea.

Rendering the Text More Effectively

The current XML Notepad is pretty rudimentary, and used incorrectly, bad things could happen. If the user cursors into a tag, and edits it incorrectly, the text might no longer be valid XML, and we might never know it. I thought about a number of ways of making this better.

Use RichTextBox instead of TextBox

If we used the RichTextBox instead of TextBox, there would be some great things we could do. For example, we could color all the tags a different color from the text, so that the user would more readily see where not to type. We could even render the different tag contents in a different font: we could make the section titles large and bold, and the code lines could be in a fixed face, while the regular text was not.

Petzold had some useful information that might help with this. He addressed how to figure out what character was selected when using a variable-width font, and showed how to know what is scrolled on the screen and what is not, so that we could render the screen more quickly by just rendering the text that is actually displayed. That would be much more efficient than what we are doing now.

Petzold even showed enough so that we could render, not to a RichTextBox, but to a pane of our own invention without much difficulty. He even shows how to write some "unsafe" code that accesses the Win32 operations on the caret (that little vertical bar that shows up between the characters you are typing when editing.) Using these ideas, we could make a much better experience for the customer.

Keeping the XML Correct

I thought a lot about the user possibly messing up the XML. He might type inside a tag, changing a <P> to a <p> or worse, to <H2>, without changing the corresponding other end. He could get the tags unbalanced, and the file would no longer contain legitimate XML. That would be bad.

I thought about displaying the XML, not in a TextBox, but in some kind of tree browser, showing the shape of the XML in the tree, and the details of each paragraph in a separate window. It might be sort of like the Explorer, with the directory tree on the left, and the text on the right. The problem with this idea is that on the text side, we want to see all levels in the file, not just one selected one.

I thought about having two columns, one showing the tags, and another showing the text. This might be a problem keeping things lined up, because one line on the tag side might consume many lines on the text side, due to wordwrap. I thought about that for a while and couldn't really see any way to make the tags and text line up. I'd like to experiment a little to be sure.

Protect the Tags

It might be possible to catch all the cursor changes. The main way the user could mess up a tag would be to curson into one and edit it. We could change the program to detect all cursor movements (not forgetting the mouse), and keep him from going inside a tag. This might have a problem if he actually needed to go in there, but it would be safe. We could maybe add a story to insert a tag that the program doesn't know -- that would be useful anyway -- or a special command to edit a tag on purpose, avoiding doing it by accident.

Verify the Structure

It might be possible to verify the structure all the time, every time the user does anything. Suppose that on every event we parsed the XML. A very simple loop would be sufficient:

  read the file
    if there's an opening tag, push it on a stack
    if there's a closing tag, pop the stack and compare
    if they match, the syntax is OK, otherwise not.

That would be cool to write, not very hard, and it would do a good job, though not a perfect one, of ensuring that nothing is broken in the structure. There could be a red light / green light somewhere in the display, that would go red, and probably beep, whenever the structure is broken.

One problem would be a less than sign (<) in the text, but that's already a problem. We haven't had a story for it, but less than in the text needs to be changed to &lt; anyway. We know they're going to ask for that sooner or later, so maybe we should just built it in now.

TextModel as a Tree

An even better solution would be to have the TextModel be a tree of tags, rather than just a bunch of strings. That way, a "sect1" tag would have a "title" tag and a bunch of "P" tags as children. It would be easy to render the tree back to text. A simple pre-order scan of the tree should do it, elementary tree processing.

One issue would arise in reading the text back into the model. But something like the push-down processing describe up above should do it. And we could probably create the tree for the new text, compare it to the old tree, and make sure that nothing too weird had happened.

It might also be possible to check the situation on essentially every keystroke. A quick check of the line that the cursor is on to see if nothing has changed might be sufficient to allow some kind of incremental updating of the TextModel's tree.

XML Support in .NET

There's XML support in .NET as well. There are XML generation classes, though in my opinion they don't add much to the situation over and above regular string manipulation. You can validate XML with a class named XMLValidatingReader. There's an XMLDocument class that could be used as the base for TextModel instead of the tree. You can load XML into the document with XMLTextReader, then navigate through it.

There are pros and cons to using tools like the above. Certainly, so far, we've stayed away from them in this book. My reasons aren't just "not invented here". I like to learn how things really work, down at the bits and bytes, so even if we were to use some of the classes above -- and I think we'll experiment with them before the project is over -- I like to start with simple and direct solutions. In addition, it typically takes a lot of learning to get one of these solutions doing anything, especially when you don't know your way around the system very well. I've got a pretty good feeling now for how C# and .NET work, and feel that I can put together some interesting tests of these classes in a few hours. But when I was just starting out, it would have taken much longer, and I would have been learning some high level material, but missing out on the basics.

So my practice is to start with the basics. Often, it works just fine to stay there. Other times, it makes sense to move to a more powerful tool, but when we do, we have enough of a grasp of the realities of the problem to enable us to navigate the tool wisely. We'll see how things go this time.

What's the Plan?

All these ideas, and a lot more, came out of a bit of lunchtime reading over the past few days. I'd say that every one of them is worth learning about, and many of them will make our program better. There are a few that probably can't coexist: we can't use both Mediator and Adapter in the TextBox - TextModel interaction. We can't both write our own XML tree and use the .NET XML support like XMLTextReader. To find out which of these ideas is best, we need to try them both. And, of course, we do want to learn as much as we can, as fast as we can.

On the other hand, we have this customer to keep happy. If we go into a cave to learn everything we might need to know about XML and patterns, the customer isn't going to get any features. And then the customer isn't going to be happy. And then the customer will cancel our project. And that would be bad.

So what's the best way to handle learning? Well, I don't know, but here are a few ways that learning can happen:

Customer Schedules Learning Stories

This is the best possible deal, perhaps with the exception of writing a book that shows how ignorant you are and how you dig your way out of it. The customer allocates a certain amount of the team's time to learning things. Sometimes the customer might pick the things that you learn, based on what you tell them things might be good for. From what we've read above, the customer might be more interested in the .NET XML support, which goes to their problem, than they are in Mediator and Adapter.

Or, the customer might just allow you so much time per week to study and experiment with things that you feel the need to learn. This would be really good. Get paid to learn. It's hard to get this deal, however, because the customer has other things on her mind, like getting her application done.

Manager Schedules Learning Time

Another good deal for the learner is for the team's manager to make a certain amount of time available for learning, holding it back from customer-driven programming time. Maybe the customer gets to schedule four days a week, or four and a half, and the other half day belongs to the team.

This can be good for the team and for the manager, of course, because the team becomes more powerful. With a bit of judgment on what to learn, the things we learn this week can help us next week. This, too, can be hard to swing. Even the best manager can feel the pressure to make the programmers work more and more, "just this little while", to respond to some urgent need or other.

This is, of course, a short-sighted view. Overworked programmers won't learn, they'll get tired, they'll make mistakes, and quite often the project or the team will falter. But it happens every day. Extreme Programming allocates one of its dozen practices to this issue, with the practice "Sustainable Pace". This practice dictates that the team will work at a pace that can be sustained indefinitely. Sure, you'll sprint once in a while, but by and large, software development is an activity that takes place over months and years, and the team needs to work at a pace that keeps them strong and growing stronger, over the entire term.

Wise team managers provide space, time, and resources for programmers to learn. But often it doesn't happen. What then?

Programmer Schedules Learning Time

Folks, this is our profession. We cannot afford not to learn, to improve our skills. If time isn't available on the job, we still need to do it. It's hard, ain't it hard, ain't it hard (oh yes), to work on programming in our free time, after slaving over a hot terminal all day long, all week long. Yet we have to find a way to do it, or we'll be left behind.

There are lots of important things in life, our families, our friends, our communities. We have to find room for all of them. Each of us has to find the balance that works for us for learning about our profession. I've made mistakes in both directions, but usually my mistake has been to focus too much on work, and not enough on the things that were important to me, including not just learning but the more important things of family and friends. I'll try hard not to make that mistake again. You -- well, you have to find your own balance.

Yes, But What's the Plan?

Enough preaching. We see here that in a few hours, I found out a lot of things that would be good to know about, and got a lot of ideas that would make the program better. How am I going to put them into effect? Here's my plan, my balance:

I'm going to try a couple of quick experiments with the .NET XML classes, probably XMLTextReader. There seem to be a couple of quick-looking tutorials on it, so I should be able to do something in an hour or so, including the writeup. I'll do that by writing some tests, of course. The test classes will document what I've learned, for later on when and if I want to put what I've learned into the system.

The ideas about validating the XML, beefing up the TextModel to some tree structure, reading every character into the TextModel to keep it up to date, using a RichTextBox, protecting the tags ... I'm going to do none of that until the customer stories require it. The code we have is good enough for what it does now. I'm sure it will need to evolve, and I suspect that some upcoming customer stories may even push it in some of the directions I'm foreseeing. But I'm not going to fall into the trap of saying "it will be cheaper if we do it now". Sorry, wrong. The code isn't getting any harder to change while I leave it alone, and I'll make it better when the time comes to edit it. So even though I'm very interested in trying some of these ideas, I'm not going to invest customer time in putting them into the system until I have a customer story that justifies it.

There were some other ideas that I'll work on when I get there. The idea for a File object may show up in two efforts that I know are coming up. Chet and I have a hanging story to do file load and save, and it may show up there. If the forces so indicate, we'll refactor in the direction of a File object. And I have been doing a little experimentation with .NET's FileSystemWatcher class, in conjunction with code manager ideas. The File object might come up there, if we get any more code manager stories.

Your choice might be different. You might choose to push on some of these ideas harder, and to put them into the system. That's OK. I'm comfortable that I have a strong focus on learning, and I'm not afraid that the ideas will get lost. Your comfort zone may be different. Just remember that the comfort zone of your customer, or your manager, might be different as well, and keep your eye on the ball, which is shipping well-factored, well-tested, high-quality software to your customer. To do that now and forever, you need to find the balance between completing stories, and learning. In this book, you're getting a look at how I try to find my own balance. You get to find yours.

XProgramming > XP Magazine > Adventures in C#: Some Things We Ought to Do
COLLECTED TOPICS: Adventures in C# | Documentation in XP | Book Reviews