Recently I’ve been trying to catch up with a huge backlog of my Podcasts and while listening through these recently one of them made me emotional enough to shout at my radio in the car. So I figured if this was something that had the ability to get me that emotional, maybe I should share my reasons for this with others, so here I am, I’m a sharing kind of guy (unless it’s my round).
The topic in question that got me shouting at my radio and making fellow commuters stare ahead more intensely than before was Object Orientated Programming, more specifically the use of some of the basic data OO patterns within Coldfusion.
I must state here that I am still a long way behind catching up with the backlog of my Podcasts and if the points I raise here have been raised or addressed in later editions of the Podcasts in question then I apologise for any overlap of thoughts or information.
Anyway, back to the post.
I’d just finished listening to Helms and Peters Podcast episode 26, Groupthink, in which Hal has what you could call a little rant about developers who use only the most basic OO design patterns, mostly being the data-centric patterns such as Data Access Object, Value Objects/Data Transfer Objects/ Beans & Gateways and declare themselves to be OO developers.
I couldn’t have agreed more, although these patterns are the workhorses of most web applications they are a huge step away from really digging into the domains you are dealing with and modelling them effectively within your software. They are in fact patterns that are used very heavily in the applications I deal with, but they are so boring and repetitive that I make as quick of an overview of the patterns when introducing our new developers at work to our applications. It takes two minutes to get your head around them and then move onto the more interesting patterns.
As an aside, this is also part of the reason I am researching CakePHP for my personal PHP based work and Reactor for my Coldfusion work. I’m really sick of creating DAOs in particular, OK so I’ve got the process down to the bare minimum in CFEclipse using snippets, and I’ve got a sweet generic persist()
method in the DAOs that, combined with the dirty marker pattern, deals with whether to call the create
, update
or delete
method as appropriate, but I’ve had enough now, I’ve got better things to do with my time.
Back in my car I move onto the next Podcast on the list, which was the Coldfusion Weekly Podcast Version 1.10 Safari Edition where the topic was a design pattern Safari; an introduction to design patterns covering DAOs, Gateways, Beans and Services/Managers.
As an introduction to design patterns these are probably the easiest to cover, as they are so simple and anyone who creates a web application will probably need these. It was kind of a co-incidence that the subject of these two Podcasts overlapped a little. The Podcast was a quick and easy introduction to design patterns and why to use them and as I say covered a few of the basic ones.
Now to the real point.
Talking about Beans and persisting data the Podcast covered the common argument of “this seems like a lot of overhead to just get at my data” by making the following points:
- Getters and setters give you lots of control over access to the data.
- You may want to make some information read only, which can be accomplished by making the setters private.
- You may want to restrict access to data for certain users/groups etc. (although I believe the Bean shouldn’t really know about that).
- Validate data passed to setters.
- Allows Bean composition. A Person Bean could contain one or more Telephone Beans an Address Bean etc.
- Beans and especially composite Beans are consistent with the way you think about the data relationships in the real world (this is touching on domain modelling).
- Easier to swap persistence methods (e.g. database or file system etc.), this was covered when they discussed DAOs, but also applies to Beans.
There are some points I would like to add to that list:
- The rest of your model and application(s) don’t need to know the structure of the database, how to deal with result sets from the database etc. just the interface of the bean.
- Your getters can encapsulate regular data manipulation tasks. For example say you have an Address bean with the fields: address1, address2, city, county, postcode. A common requirement for addresses is to format them as a concatenated string (usually with commas and spaces) while ensuring some of the optional data (e.g. address2 or county) is handled correctly. In my opinion this kind of functionality really deserves to be encapsulated in a method on the Bean such as
getFullPostalAddress()
.
So this was great, a fine example of defending what some beginners in OO see as a un-necessary overhead to “just get at the data”, fantastic - if I wasn’t sold on Beans before I would have been after listening to that.
After that they skim over DAOs, again giving you the right amount of information about DAOs.
Next is where it all went wrong for me, Gateways. They jump in head first stating with CF they return queries from their Gateways rather than arrays of Beans because it’s easier and quicker (CF does have some sweet query iterators) to use these rather than Beans.
“Hold on!” You may gasp (or shout at your car radio). “Haven’t they just spent the last few minutes extolling the benefits of Beans, their encapsulation of data and powerful yet simple interfaces to said data etc.”.
If you didn’t, you should have and I believe that you would be right to. Not only do we lose all those benefits of Beans that have just been covered, we now have the possibility of having to write two types of views (in a Model View Controller based application) to do the same thing depending on whether we’re dealing with a single entity or multiple entities and lots more problems. It’s all so messy.
So why are we in the position where it’s become common practice to have Gateways returning query objects willy-nilly in CF and why did we lose sight of the benefits of Beans so quickly?
Well with OO in CF we currently (fingers crossed this can be resolved) have a small problem, instantiating objects adds quite a lot of overhead. I can’t remember the exact figures, and it probably isn’t a consistent amount for all objects, but somewhere around 40ms is something that I remember being quoted to me. This is obviously a problem if we want to return lots of populated Beans from a gateway as an array, just instantiating 10 of them will add nearly half a second to your execution time.
This wasn’t really covered in the Podcast but really deserved to be. Before I continue I must say that I am myself guilty of using Gateways to return query record sets, basically because I didn’t know any better, but it is something I intend address in the near future.
So what can we return from Gateways in Coldfusion?
The absolutely fantastic (and prolific) Application Generation blog by Peter Bell offers what I believe to be the best solution I’ve seen so far, Iterating Business Objects.
The basic principle is you have a Bean (Business Object) that knows how to take a query from a Gateway and then iterate over the query populating itself on request. This way we retain the power of the Bean but don’t add the overhead of object instantiation and everyone is happy, although it’d be nice to retain something as simple as
I was planning to say a little about this idea and link to all these fantastic posts from Application Generation and now seems the perfect time to do so. I’m not going to re-hash the detail from the posts but they are all definite must reads for any CFer:
- Returning data Beans vs Queries
- An Iterating Business Object
- Real World Benefits of Iterating Business Objects over Recordset
- Snippets The Base Iterating Business Object
- Snippets Displaying an Iterating Object
I hope this post doesn’t come across as me bashing the Coldfusion Weekly Podcast, because that was not the aim - it’s one of my few regular Podcasts I listen to. My aim was only to point out that when it comes to Gateways I think we can all do a lot more to work around some of the limitations of CF without losing the benefits of Beans.