Archive for February, 2008
It’s always funny how people, at this day and age, still look at OOP and Design Patterns as some sort of "new thing that probably Microsoft created and is trying to shove down our throats", even though those are things that have been around for decades now, and still, a lot of people don’t really quite get it.
The "new" thing now seems to be the MVC (Model-View-Controller). I must confess the first time I heard about this pattern has been just a couple of years ago (like 3 or 4 maybe…). Last night, I stumbled across this article, where I’ve read the following:
The MVC pattern was implemented as early as 1974 in the Smalltalk project. It has been given, over the years, many variations, such as Model-Interactor and Model-View-Presenter. Each of these implments the idea of ports-and-adapters on the primary ports, not the secondary ports.
1974?? That was two years before I was even born!!! Didn’t know it was that old. 🙂
The sad thing is that I’ve been thinking a lot about how to put together something along those lines to work in smart client apps, but it isn’t quite simple. MVC, MVP, Supervising Controller, Passive View, they’re all related but have subtle differences. they’re all, however, the total opposite of anything Visual Studio gives us out of the box, like I was mentioning the other day…
Yesterday I’ve finally finished reading the following book:
|Working Effectively with Legacy Code (Robert C. Martin Series)
by Michael Feathers
I had already heard great comments about it, and then over a month ago my buddy Lynn Evans told me I had to read it and let me borrow his copy. I’m glad I did.
I must say when I first saw the title of this book, I thought to myself: "hmm… it’s probably all about old C code, mainframe applications, etc. I don’t think I’d be interested in this book…". Boy, was I wrong…
Yes, there’s quite a bunch of sample code in languages I’m not particularly comfortable with, such as C++, Java, and C, but anybody used to C# or VB should be able to follow the examples without a problem (not that I wouldn’t like to see a C#-only version of this book, but that’s beyond the point…).
Even though some people, like myself, would read the "legacy" in the title and think that wouldn’t apply to code written as back as this morning, the author quickly explains what legacy code is: any code without test is legacy code.
According to the author, it doesn’t matter how badass the design and implementation of some code may be, or how killer the developer who wrote is; if there’s no tests for the code, it’s considered legacy code.
When I think of legacy code, I think of code that’s hard to maintain. It’s hard because I don’t know what impact any change I make to the code would have on the application. Since there are not tests, even a one-liner change can be dangerous; let alone some more major changes.
Even before the first chapter starts, I’ve already run across this gem:
Designs that cannot tolerate changing requirements are poor designs to begin with. It is the goal of every competent software developer to create designs that tolerate change.
The book covers many techniques for writing tests for code that hasn’t been written with tests in mind. It tackles typical questions such as "how do I test private methos? And should I?". It also presents considerations on removing dependencies so to be able to put the code under test, as we do that, we usually end up with code that’s easier to maintain and extend. All of this going through lots of refactoring techniques.
I’m glad to see that quite a few of the techniques I’ve figured out on my own are listed in this book, with samples that come from real world experiences.
Excellent book, recommended to any serious developer.
This is the sequel for this post. In this installment I’m going to cover a simple scenario that I’m always running across: a simple data-entry form, and the synchronization of its buttons used to load, create, or save data. The sample form I’ll cover here is pretty simple, and it looks like this:
That’s nothing more than a simple form to maintain Customer’s data. I’m just capturing the customer’s name, because the amount of data gathered here isn’t relevant to the exercise. The main thing I’m looking for here is the synchronization of the Load, New, and Save buttons. For instance, when the user clicks New, I want all the buttons to become disabled. As soon as the user makes any change to the data on the form, the Save button then becomes enabled. Then, when the user clicks on Save, both Load and New become enabled, whereas Save becomes disabled. Of course, there’s a lot more buttons we’d normally have in such form (such as Delete, Search, etc.), but that’s beyond the scope here.
The quick implementation would be to just go on the form and add some event handlers for the buttons, in which we’d enable/disable other controls accordingly. However, eventually we may want to move those buttons somewhere else (maybe put them a user control, as opposed to directly sitting on the form, or maybe put them in a toolbar or Ribbon control…). Also, we may want other ways to access the functionality (for instance, we may want to press Ctrl+N for New, and Control+S for Save…). The problem is that if all the logic is in the form, we’d have to put too much code in there so to handle buttons being somewhere, shortcuts, etc.
I decided to look at the form as it having different "states": for instance, a "viewing" state, which is the case when the user is viewing some data, and a "dirty" state, which is the case when the user has made changes to the data but hasn’t saved it yet.
Enter the State Machine
The following State Transition Table lists the simple states I’ve identified for my simple form:
|Current State||Event||New State||Action (transition)|
Below is a State Diagram to present another view of the states:
Using the State Machine Toolkit (discussed in Part I), I’ve created a state machine to handle those states. I’ve named the machine DataInterfaceInteractionBase (the idea being that the machine handles the interactions in a "data" user interface…). My default concrete implementation of this machine is called DataInterfaceInteraction. fThe diagram below shows what these classes look like:
The DataInterfaceInteraction class implements the abstract methods (TurnOnDirtyIndicator and TurnOffDirtyIndicator), as well as it overrides a few of the virtual methods (EntryDirty, EntryInitial, EntryNew, EntryViewing). The methods delegate execution to an IDataInterfaceInteractionController, like so:
The diagram below shows the relationship between the state machine and the controller:
Notice the following aspects of the IDataInterfaceInteractionController:
- It has methods that match the ones on the state machine (such as EntryDirty, EntryNew, TurnOnDirtyIndicator, etc.);
- The SetHost method is designed to take in a reference to the state machine that the controller handles;
- The TurnOnDirtyIndicator and TurnOffDirtyIndicator methods raise the TurningOnDirtyIndicator and TurningOffDirtyIndicator events, respectively;
- The RegisterInterfaceController method takes in "data interface controllers" and register them with the Interaction controller. Any number of interface controllers can be registered.
The constructor for that class looks like this:
It takes in an interaction controller, as well as an array of data interface controllers, and then uses those references to register things accordingly.
This is what the IDataInterfaceController looks like:
The properties essentially dictate whether Load, New, and Save features should be enabled or not. The SetInteractionStateMachine method takes an instance of the the interaction state machine that this controller is associated with.
The DataInterfaceInteractionController class
Next, I’ve implemented my default DataInterfaceInteractionController class, which we attach to the interaction state machine. Let’s look at the diagram to see how things are related thus far:
Notice the class has a DataInterfaceControllers collection, which is a Collection of IDataInterfaceController implementers. Nothing too fancy there. The RegisterInterfaceController method is used for adding controllers to that collection:
More importantly, the methods such as EntryViewing and EntryDirty (which get called when the state machine is entering those states), iterate through the data interface controllers and set the LoadEnabled, NewEnabled, and SaveEnabled properties accordingly:
Notice, for instance, that when the user is "viewing" some data, both Load and New are enabled, while Save is disabled. When the user then makes changes to the data, causing a transition to the Dirty state, both Load and New become disabled, whereas Save becomes enabled. In the real world we’d probably have more complex implementations so to decide whether or not some features are enable or disabled (for instance, we may have security restrictions depending on the user, or any other sort of business rule).
At this point we’re only determining which "features" should be enabled or disabled depending on the current state we’re in. That means we’re only programming the behavior here. We don’t have any code at this level that is enabling or disabling buttons, keyboard shortcuts, or anything like that; we leave that up to the implementes of IDataIntefaceController, which will take a look at next.
Hooking up the CustomerEditForm to the Interaction state machine
The CustomerEditForm has an Interactions property, which stores a reference to the DataInterfaceInteractionBase class (which is the abstract baseclass for the interactions state machine):
Setting up the Data Interface Interaction Controller
Next, I’ve created a GetInteractionController method, which instantiates the interaction controller, and sets up event handlers for its TurningOffDirtyIndicator and TurningOnDirtyIndicator events:
The event handlers are just simple anonymous methods. All we’re doing there is to display a little asterisk (*) after the title in the form’s title bar whenever the data is "dirty":
Sending events to the State Machine
It’s necessary to send events to the state machine, so that it can transition to the appropriate states. We’ll first use the buttons located on the customer edit form to send those events:
Since I’ve based my state machine on a "passive" machine, anytime I send events to the machine (by calling the Send method and passing in the event id), I also have to call the Execute method. I created a simple helper method around these calls:
Next, I’ve hooked up event handlers for the Load, New, and Save buttons. Those handlers are very simple, since all they do is to send events to the state machine. I’d also handle any event that would warn me about the data being dirty (such as TextChanged events in this form…):
The first Data Interface Controller
Remember that the Data Interface Controller is the object that enables and disables the features (new, load, save…) accessed by the interaction state machine. We can have as many data interface controllers as we need.
The first implementer for the IDataInterfaceInteractionController interface is the customer edit form itself, since it hosts the buttons that send events to the state machine. The implementation of the interface is as simple as this (one property for each feature):
Nothing to exciting, right? Just some interaction with the Enabled property on the specific buttons.
I’ve then created an InitializeInteractions method, which I call from the form’s constructor. The method looks like this:
A few considerations:
- I call the GetInteractionController to get an instance of the Interfaction Controller that’s to be associated with the interaction state machine;
- I handle the TransitionCompleted event on the state machine (the event handler, shown below, just listens to transition completion on the state machine, and updates the status bar on the customer edit form accordingly);
- The state machine is then instantiated, with two parameters passed to the constructor:
- a reference to the interaction controller;
- an array of IDataInterfaceController objects (which at this point only has a reference to the form itself, since it does implement that interface).
This is the handler for the state machine’s TransitionCompleted event:
At this point, the form is operable. Now I need a few other interface controllers.
Triggering the actions with some fancier buttons
Let’s say that instead of having Load, New, Save buttons placed directly on the form, we wanted to create a fancy user control containing those buttons. Or maybe the buttons were sitting up at a toolbar or ribbon control. It doesn’t matter. The point is that now the form wouldn’t own the buttons anymore. I’ve went with the special user control for this example:
The user control itself (I’ve named it SimpleDataButtons) is a very simple one: it just has the few buttons sitting on it, and some properties were set just to make it look "fancy" (yeah, right…). The class implements IDataInterfaceController, and the implementation is pretty simple:
The SetInteractionsStateMachine takes in a reference to the state machine that this controller sends events to, and the properties (NewEnabled, LoadEnabled…) simply interact with the Enabled property on the controls.
The event handlers for the buttons look pretty much the same as we had on the customer edit form; it just sends events to the state machine:
Now all that’s left to do is to drop the SimpleDataButtons user control on the customer edit form (I’ve named the field simpleDataButtons), and add it to the array of IDataInterfaceControllers:
That’s it. Now when I run the form, I get both set of buttons, and they both work interchangeably (of course, in a real app, I’d have either one or the other…).
Adding shortcuts to trigger the events
Another interface controller that I wanted to add is one that handles keyboard shortcuts. Essentially, I want to access the New, Load, and Save functionality by using Ctrl+N, Ctrl+L, and Ctrl+S, respectively. With the current architecture, this is pretty easy. The "trickiest" part is probably figuring out how to actually handle the keyboard shortcuts. In fact, not too long ago I’ve posted a little design and implementation to do just that (make sure to check out that post!).
For the scope of this post, all you have to know is that I’ve created a DataInterfaceKeyboardHandler (which handles trapping the keystrokes and executing some action depending on the shortcut mapping), and this class implements the IDataInterfaceController interface. Besides receiving a reference to the state machine that the controller sends events to, the implementation of the NewEnabled, LoadEnabled, and SaveEnabled properties essentially does the following:
- The getter returns true or false, depending on whether or not the specific shortcut is registered with in the list of shortcuts;
- The setter, registers or un-registers the specific shortcut, depending on whether a true or false is passed to it.
Again, all the shortcuts do is to send the appropriate events to the state machine. Once again, make sure to check out this post to understand how this DataInterfaceKeyboardHandler class handlers registering, un-registering, and executing keyboard shortcuts.
At this point, it’s only a matter of adding an instance of the keyboard handler to the customer edit form, and add it to the array of IDataInterfaceControllers:
If I run the application now, I can either use the buttons or the shortcuts to access the functionality.
One quick improvement to this design and implementation would certainly be a more generic way to register interface controllers with the data entry forms, but I’ll leave that as an exercise to the reader.
Another improvement would be to add functionality to the State Machine Toolkit’s code generator so that, besides generating the state machine, it could also generate the interfaces for the both the interaction and data interface controllers, as well as a default implementation for the state machine (delegating actions to the interaction controller).
I’m going to also, definitely, clean up names so to make the intention of things clearer, and clean up the design as much as possible.
I also want to play with Windows Workflow Foundation (WF), since it has the option to create state machines. Ideally, it’d be good if I could swap what state machine engines I’m using, without having to change anything else in the application.
Bringing this thing to the point where I have it right now has been really fun. It allowed me to play with and exercise a lot of cool things, such as state machines, delegates, anonymous types, commands, shortcuts, interfaces, controllers, etc.
I’ll keep working on this thing as I move some of this knowledge into some existing applications, and will certainly post anything else I may that could be interesting for somebody else (or may just as reminder to myself…).
Downloading the source code
The other day I posted about how I’m now using a VPC machine as my main working machine.
One thing I wasn’t totally thrilled about it, though, is because we can’t benefit from dual monitors when working with the VPC machine. Today I ran across this post, which explains a simple workaround for the issue. There’s also another good post here with a little more details on this. The thing that got me is that I haven’t thought of before is that we can open a remote connection to a VPC machine. How come I didn’t think of that before?! 🙂
Anyway, this is really cool.
Also, I’ve considered using MaxiVista so to make my laptop’s screen as my third monitor, but as it turns out it doesn’t work with the video driver that I have on my machine. Too bad…
This Thursday, February 21st, I’ll be speaking at the North Houston .NET user group.
I’ll be talking about Test-Driven Development (TDD). And I heard they got good prizes there, so you may wanna come check it out. 😉
I’ve migrated to Vista about 6 months ago. At some point, Visual Studio started to run really slow. Quite often, I’d just click somewhere, say in a line on the code editor, and VS would just freeze for minute or two, for no apparent reasons.
At some point I thought it could be because I have a bunch of plug-ins installed (CodeRush, RefactorPro, GhostDoc, TestDriven.NET, etc.), even though I didn’t have such problem on Windows XP. I tried removing all plug-ins and giving it a shot; no luck there. It was still behaving the same way.
Next I thought maybe it was a memory issue, since VS is always taking up 500Mb or so. My machine had 2Gb, so we threw in another 2Gb. I also always have a 2Gb ReadyBoost USB drive plugged in. But nope, that didn’t help; VS kept freezing up on me.
I’ve heard of people using a Vista box just as a VPC launcher (that is, the Vista box wouldn’t have much things running on it, and it’d only serve as the means to launch Virtual PC images). I’ve decided to give that a shot. I had actually worked with VPC machines to work as build machines, and also for presentations using beta versions of VS, but haven’t really used them to do actual work. Oh, I have also used it for some debugging.
I went ahead and created a VPC with Windows XP as the operating system, and with just enough things installed on it so that I can do development work, such as Visual Studio, my VS plugins, and client tools for SQL Server (such as Management Studio, but not the actual database server). I have not installed anything else, such as MS Office or things like that. I assign 2Gb of memory to this VPC machine.
As it turns out, working on a 2Gb WinXP + VS VPC machine is a much more pleasant experience than working on a 4Gb Windows Vista + VS + Office + etc machine.
There are other good benefits of working with VPC machines like that:
- Changes can be easily rolled back by closing the machine without saving its state. This is very helpful, for instance, when you install a plug-in, or any other software for that matter, that messes up with the environment. If that happens, it’s pretty quick to just roll back. Also, if you’ve made a bunch of changes to files and things that get you to a pretty messed up state, just roll it all back. For instance, sometimes I open the VPC machine and I have some files checked out in VS from my previous session. Then I make a bunch of changes and I’m not too happy about it, I can just roll back the machine’s state and start all over.
- I can easily work on any machine. Since I’ve put the VPC machines on a small USB drive, I can just plug it in to any computer that has Virtual PC installed, and get down to business real quick. This means that if my main computer happens to go through a big crash, I can be back up and running in no time.
This seems to be a pretty good way of to do this, and I’ll keep pursuing it. I have on my to-do list an item for researching some more into how to work with VPC machines effectively (I’m know there’s a LOT of things I need to learn about that, but I do what I can with the time that’s available…). 🙂