By Peter Bell

Partially Loaded Objects

I mentioned some time ago that I will often load up my iterating business objects with only a subset of their attributes and I remember being questioned about the approach at the time. I want to make a quick post explaining why I believe partial object loading is a very appropriate technique and one that is almost essential for acceptably performant applications when using an iterating business object.

There are two compelling reasons to use iterating business objects instead of query recordsets for returning lists of data. The first is that you can encapsulate the getting and setting of all attributes so you can display a users age (even though the database only contains their date of birth) or you can display a products actual price (which may be calculated using complex rules based upon multiple field values from the database). All of the calculations are now stored nicely in User.getAge() and Product.getActualPrice(). Clean, easy, best practices. The second reason is that you can use exactly the same encapsulated getters are setters whether you are working with a single record or with n-records which makes sense from a DRY (Don’t Repeat Yourself) perspective – the way to calculate the age of one user is the same as the way to calculate the age of n-users.

So, I think iterating business objects are a great solution for any application where the required attribute values often need to be calculated (which fits my use case perfectly). But clearly if you want to list the SKU and price of fifty products per page it makes little sense to get all of the data associated with each of those products and then just to display two fields (it would be like using a SELECT * for all of your list queries – not a best practice). Because of that, I allow an AttributeNameList property on all of my get methods which allows you to return only the required attributes rather than fully loading the object.

Of course, as was pointed out to me, if you were to go to a partially loaded object and ask it something it hadn’t loaded, it wouldn’t know the answer (if you asked the object loaded with price and SKU what its detailed description was, it wouldn’t know). However, in practice that is not a problem. If you used a gateway/recordset approach and tried to output a cfquery that only contained price and SKU you would also not be able to output the DetailedDescription field.

The only small wrinkle if you want to automate the generation of all of this is that you need to have an attribute to field mapper so that (for example) if you want to display the Age attribute, it will know to SELECT DateofBirth and if you want ActualPrice it knows to get SalePrice, DefaultPrice and DiscountLevel), but I’ll go into that in more detail later.

Comments
Do you not find any performance hits when you have to create a component for every returned record? I have worked on code written by others that works in this manner (though quite crappy... lots of select * and what not) and I have seen that page load times can be noticably slow. I am not talking like waiting around for things to happen kind of slow... but more like 'that's taking longer than it should' sort of slow. But, like I said, the code I was working on had LOTS of problems, so I can't be sure what part of it was causing the problem.
# Posted By Ben Nadel | 9/17/06 2:20 PM
Just had a specific scenario thought. I have a page on my site, where I list out all the blog entries on a single page. This is done more for my pesonal needs and for a good search engine spidering page. There are several hundred blog entries there already:

http://bennadel.com/blog/complete-blog-entry-list....

Now, there is really only three pieces of info that I need here: title, date, and link. The way I do it now is a get one query of all active links. Then I query of query that for distinct year / months. Then I loop over the months and do a query of query for each month (to get entries for that month) and out put those queries. The page loads quite fast IMO despite the large amount of data and some Q of Q action.

The question then becomes, if I had to do that using an iterator and create an object for each record to get the title, date, and link:

1. Would that be a large performance hit for all those object instantiations?
2. I would not be able to do any query or querying and therefore would haved to change my style... what style would you recommend?

Thanks Peter. Just want to accomplish this as best as possible.
# Posted By Ben Nadel | 9/17/06 2:25 PM
Hi Ben,

That is my whole point. I am NOT creating an object for every returned record. Lets say we're working with products in an e-commerce application (as an example). If I want to display a product detail screen for product 12, I go get product 12 (a single record), drop it into a single iterating business object and display it. Only one object created.

Equally, if I do a search that returns 45 products, I take that one query with 45 products and drop it into a single iterating business object (same one as above - no difference) so again I am only creating a single object.

Theoretically if I wanted to display 1000 products on the page, the query and HTML display would probably run a little slow, but my component instantiation cost would be the same - I'd just be creating a single object.


All the iterating business object is is a place where you can load a query into a struct and then access the values via getters and setters so if you want to have calculated attributes (user age based on date of birth, product actual price based on sale price and regular price), you just write getAge() and getActualPrice() methods and they are available every time you work with a user or product - whether there is one record or there are many.

This is relaly a fairly ColdFusion specific hack. In Ruby or Java the object instantiation costs are so much lower, I would instantiate a collection of objects, but in CF that can run a little slow so I suggest the iterating business object as a high performance solution with all of the benefits of a true collection of objects. It is also really simple! Make sense?
# Posted By Peter Bell | 9/17/06 2:27 PM
Hi Ben,

One solution would be using an iterating business object (you'd only create a single object for all of the hundreds of records in the recordset). You would only load it with PublishedDate and Title (see my next posting on loading objects using an ORM - it is right on point for this. I would then add a custom getter for this object (lets call it a Posting object) getMonth() would return the month of the posting.

You would then just iterate through the business object and a simple solution would be something along the lines of:

<cfset LastMonth = "">
<cfset Posting.first()>
<cfloop condition="NOT #Posting.isLast()#">
<cfif LastMonth NEQ Posting.get("Month")>
#Dateformat(Posting.get("Month"),"YYYY/MM/DD")#<br />
</cfif>
<li>#Posting.get("Title")#</li>
<cfset LastMonth = Posting.get("Month")>
<cfset Posting.Next()>
</cfloop>

Obviously I'm not opening and closing my <ul>'s correctly and the date format mask I used isn't the one you need, but does that give a sense?
# Posted By Peter Bell | 9/17/06 2:36 PM
So this single iterating business object in your comment above would then store the query that returns the all the blog entry records and your isLast(), first() and next() functions would be looping through each recordset and updating the values you get back with your get() calls?? Is that right?? But from looking at this approach whats the benefit here, isn't just better to loop through a query as all you are using is a reusable get() function rather than a specific getter to encapsulate the specific data you want back (for example, getTitle() instead of get("title"))?? Curreently, what I use are the get() and set() functions in my objects as they all inherit from a base class.
# Posted By Javier Julio | 9/17/06 5:15 PM
Hi Javier,

My reusable getter is a *special* reusable getter :->

If you call User.get("Title"), the first thing it does is to check whether the user object has a getTitle() method. If it does, it uses that custom method with whatever calculations you created. Only if you don't have such a methods does it get the data from the underlying srtructure (which was created from the query). In this way, if you have an object with 30 attributes of which two are custom and 28 are not, you don't have to write the 28 standard getters and setters, but you can write the two custom ones, getting all of the benefits of encapsulated access without having to write piles of default getters and setters - best of both worlds.

Even better, if you are serious about information hiding, I have a version of this object that also has a gettableAttributeList() and settableAttributeList() and it won't even try to get or set an attribute that isn't on the appropriate list.

Lets say you have a user list with a SELECT FirstName,LastName,DateofBirth but you want to display FullName and Age, gettableAttributeList = FullName,Age (of course, you're going to have to write getFullName() and getAge()) and if someone tried to get("FirstName") it won't let them, providing great information hiding. However on your edit screen your form would have firstname, lastname and dateofbirth as long as they were in the settableAttributeList as those are the things you can actually save to the db. Very flexible, very little code!
# Posted By Peter Bell | 9/17/06 5:55 PM
Peter, I like the idea of the single iterator that loops over the query object and then provides the interface to the query record as though it were a collection of single objects. That's the kind of stuff that I would never have thought of. Top drawer my friend, top drawer.

I have to say that I am not crazy about the suggestion about iterating over query and checking to see if the current month is the same as the last month. I used to see that sort of think for parent-child indenting relationships... and I have to say, it just never sat well with me. I don't quite know how to explain the uneasiness I had with it. It just involved too many IF/ELSE statements. The thing that I love about the ColdFusion query of queries approach is that there is no IF logic. You are just looping. Things start at the beginning of the query loop and end at the end of the query loop. The whole thing just felt so much more explicit.

But I suppose stuff like that is part of the trade off of creating tighter code.

Thanks for the suggestions about the single object iterator though. Very cool again!
# Posted By Ben Nadel | 9/17/06 7:15 PM
Hi Ben,

Glad you like the good idea (iterating business object).

I also agree the other idea feels like a bit of a hack, but it is sooo much tighter than the whole QoQ things and it fits well with this approach, so I'm prepared to live with it. I'm actually doing some work on UI controls and I may try to abstract some of this kind of functionality into a group by property into a list control so at least you'd jus have to set a group by field and wouldn't actually have to look at the ugly code! Will keep you posted, but first I've an ORM to finish up!

Adios
# Posted By Peter Bell | 9/17/06 7:29 PM
Ahhh very clever indeed Peter! How do you test if that custom getter method exists?? Using a cftry/cfcatch or sifting though getMetaData?? ...Well know that I think of it a method in a CFC would just be part of its Variables scope, so couldn't you just do a structKeyExists() on that method to see if it exists?? So for example, if I do get("Title") it can run a structKeyExists(Variables, "getTitle") to see if that method exists?? Again a brilliant approach that never crossed my mind.

Currently, I use my base class for the generic getter and setter, built in validation, and the same thing you metioned at the end, which I have set up as a setOnlyExistingVars, setExcludeList, and getExcludeList. The first is self explanatory but the other 2 variables will prevent a list of variables (object properties) that I will allow or not allow someone to interact with. Its really for the development side only. But I wholeheartidly agree its very flexible and very light in code thats why I'm very excited you'll be having some downloads soon as I have seen the benefits of DRY, base classes, and generic getters and setters.
# Posted By Javier Julio | 9/17/06 8:29 PM
Hi Javier,

I don't like to cftry/cfcatch unless I really have to and I'm not a fan of needless complexity, so knowing that methods are first class variables from Hals great work and Seans great presentation on Mixins, I do exactly what you suggested. In my generic getter which has a single required argument called AttributeName, the if statement is:

If (structKeyExists(variables,"get#arguments.AttributeName#"))

Simple as that!
# Posted By Peter Bell | 9/17/06 8:38 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.