A collection of learnings and opinions.

Thursday, March 1, 2007

I'm Thinking of Something Round

I was having this little problem with the app I'm developing. It's the same program that I've been talking about the last two posts, the unnamed application I'm calling Pluto. The problem I'm addressing this time is not domain-specific however, but rather a result of my lack of experience with the platform and language I'm using.

I was looking to present parameters in a GUI for the user to manipulate. I'd solved the tasks of getting the parameters from the web service and presenting them in controls based on their type. Getting the parameters into the user's face is only half the work, though. I also had to get the user's input back into Pluto's guts somehow. Now, this gave me a few sorts of problems.

The first problem is that I know the user is interacting with a control, and I know which kind of control it is. Thus, one might think I could easily map the control-type to the parameter-type. It works, for dates, strings and booleans. It's not so easy for integers and floats though, as they're both most easily displayed with what .net calls a NumericUpDown (lovely little things). As you may know you can set the number of decimals such a control allows.

I briefly toyed with the idea of setting integer-type controls to have 0 decimals and floats to have some other number and parsing based on that. All this was starting to smell of a hack. I was taking some arbitrary subset of the data contained in a datatype (the original ReportParameter), using it to construct some other object (the Control) and then trying to make a new object of the original type with that small subset of data and some implicit knowledge (what kind of control each parameter type mapped to). Badness.

How do I get out of this bad situation I've gotten myself into? It's obvious that I want to keep the original object, and just get the changes (if any) from the control into it. That would be much better, safer, cheaper (well, not in memory but we're talking about on the order of 0-10 objects here) and much, much less buggy. Just imagine trying to maintain such a mapping if you've really no idea what I did and why...

How do I keep the original objects? One solution would be a global list of the original objects with the same numbering as the controls I added (or some other arbitrary scheme to keep the control-parameter mapping). This is not an ideal solution - it's not very object-oriented, the mapping is non-obvious and globals tend to be a bad idea for these kinds of problems. So, I kept thinking.

When I keep thinking I can come up with some really convoluted and complex solutions to problems like this. My though was this: If I make an object that includes both the original parameter and the control I can just ask it to return the parameter. The immediate problem with this is that I would loose the elegance of adding controls directly to the GUI. Could I somehow solve this by making the object an extension of the Control? If I were a .net -guru I could possibly do this in some way and easily make such an object. I am not, however, I am brand new at this platform and totally new at VB.NET. So, I looked around, read a bit, tried to understand how this plot could be hatched, but I really got nowhere.

This got me thinking about patterns, though. I like patterns, so I tried to identify what I was trying to do. Was my problem solved by a proxy? Well, no, I didn't really need an object to stand-in for another. I had access to the control, I wanted to add functionality. Could it be a composite? Not really that either, as composites are good at unifying the way you use some number (usually a substantial number) of components. It had to be a decorator, the pattern that lets you add some functionality to some existing object (by "painting" that functionality onto it). It may seem obvious now, but it's never that easy when you're thinking through it. At least it isn't for me, caveat emptor.

I had it licked. I just had to implement the pattern that "painted" the original object onto the controller. Why was I not happy? This was a tiny, but important part of Pluto, and going straight on to implementation of a decorator-pattern with interfaces and implementations et cetera wasn't what I wanted to do. I still had a hope, maybe just a wish, that I wouldn't have to go through that for this simple need.

I'd skimmed the definition of the Control to see if there was anything I could use in there at the beginning of this problem, but nothing particularly stood out. I was probably just putting off starting on the pattern, but I found myself reading through it again. I was scanning for any kind of Object-reference the Control could keep - maybe I could just put it in there? I found a few, but they all sounded like they changed the behaviour of the control in some significant manner (AccesibilityObject? No, I don't like the sound of that). Then I found it: the control has a property called a Tag, and it's an Object.

You know that combination of elation and shame you get when you see that a problem you've spent good time on has already been solved? Not only has it been solved, but it's right there in front of you ready to be used. Let me quote the description of the Tag from the API: Gets or sets the object that contains data about the control. Could it be better?

So, slightly shamed but mostly happy about the way the problem had been solved elegantly and without any implementation whatsoever I simply tagged the controls with the parameters. This means that I can get the original object directly when I parse the input from the user, get the content that's been changed and send the whole shebang back to the server.

Right now I really love that.

My professor once told me: "A few hours in a library can frequently save you months of research."

No comments: