CFArgument: The Iterating Business Object
This is the first entry in a planned series of discussion between myself and Peter Bell where we will discuss hot topics in the ColdFusion community (thus the label CFArgument). Today's topic is Peter's own iterating business object, which has been the source of some discussion lately.
Peter Bell:
An
Iterating Business Object is a ColdFusion specific design pattern
allowing you to write OO code without worrying about the performance
overhead of creating arrays of objects in ColdFusion. In ColdFusion, if
you're not using an IBO, you're not really writing OO code.
Brian Rinaldi:
An
IBO is a workaround to a ColdFusion specific performance issue, and
that is the high cost of object instantiation. That issue has improved
in ColdFusion 8, but when dealing in large sets of objects the cost can
still be a killer. That doesn't mean that the IBO isn't a reasonable
workaround, but without this issue the IBO has no intrinsic value. For
example, if they fix performance on object creation in ColdFusion 9,
the IBO ceases to be useful.
Peter Bell:
I agree that
if object instantiation was faster, the IBO wouldn't be necessary. I
certainly wouldn't propose using an IBO in Java or Groovy, but what I
think is pernicious is the frequent use of Gateway objects that return
recordsets rather than objects. Many CF developers will return a single
record as an object and a collection of records as a recordset. To me
that makes no sense. If you have a User object and you want to have a
getAge() that calculates the age from the date of birth, that method is
equally necessary (and should therefore be equally available) whether
you're looking at one user or a hundred. Same for products with a
getDiscountedPrice() or other potentially complex calculations.
I
would argue that the lack of consistent use of IBOs means that many
developers end up with pseudo-OO code which has all of the costs of OO
programming (more files, more typing) but few of the benefits
(encapsulating data and associated methods into objects) because they
can't encapsulate custom getter logic in their beans since they are
using recordsets for displaying collections of objects instead of an
IBO. They are doing this because of the fear of the cost of the object
instantiation. That to me is the problem with not using an IBO.
Brian Rinaldi:
We
agree that using straight query results isn't optimal in all cases. I
also agree that the IBO is a reasonable response, even though it does
force you to repeat yourself a bit, putting logic in both the bean and
the IBO. It would seem to me there are potential solutions to this that
don't require code repetition. Also, one can argue that in many cases,
simple recordset data meets the requirements. Nonetheless, neither case
is optimal for remoting with Flex.
Peter Bell:
The
thing I love about the IBO is that you don't need to repeat yourself at
all. The way I use the pattern a business object is simply a collection
of n-beans where n=1. At first I thought this was a little hacky (let's
be frank - the IBO *is* a little hacky - violating the single
responsibility principle if nothing else, by duct taping an iterator
onto your business objects), but having used it in a bunch of projects,
I find that it works really well.
With an IBO, I can load up one
record and treat it as a business object or n-records and use it as an
iterator. Perhaps the most useful way of thinking of an IBO is as a
base class that your business objects can extend to allow your custom
getter, setter and validation logic to be written as if you were
writing a regular business object in Java. However, instead of creating
an array of such objects plus a separate iterator, with the few base
methods in an IBO, you simply load up an array, recordset, struct or
collection of value lists into the IBO and then get all of the benefits
of an array of business objects with an iterator - with almost none of
the instantiation cost.
The only cost is the slightly bitter
taste at having to stick iterator logic in a base business object which
I agree is hacky. But it just works so darned well!
There were two other points you made as well that I'd like to respond to briefly.
If
you genuinely have recordsets with no custom getter logic, I agree that
an IBO might be overkill. My problem is that my simple business objects
often get more complex over time so I have found for my use case using
IBOs consistently gives me the room to expand and no real extra
complexity upfront. I'd much rather use IBO's for everything or nothing
than have to try to decide on a business object by business object
basis which one was likely to become complex over time.
As for
Flex, the trick would be to have a simple routine that would loop
through all of the getters of an IBO for each instance, creating a
collection of named structs. That gives the strongly typed VO's in Flex
without the cost of creating an array of objects. I haven't had a need
to write this yet, but it'd be pretty simple to add an asFlexVOs()
method to the IBO - whether it was done in the IBO or handled by a
composed object.
Brian Rinaldi:
Well, I think we can
both agree that the best solution to this issue would be for Adobe to
remove the high cost of object instantiation. I think things like the
IBO, while useful to solve a problem, continue to solidify the idea
that ColdFusion isn't designed for real OO development.
Peter Bell:
I
agree. And there are some problems that an IBO doesn't solve. For
example, very few ColdFusion applications have a truly rich object
model with things like an Address value object for handling Billing and
Shipping addresses composed within an Order object because the cost of
object instantiation makes it impractical. It is interesting to see
that some CF developers are moving to Groovy for their model and others
are looking at other implementations such as Railo where object
instantiation appears to be much faster. Hopefully amongst all of the
great new features in CF 9 there will be some engineering time left
over to address the issue and make ColdFusion a compelling language for
creating dynamically typed OO applications on the JVM.
"That doesn't mean that the IBO isn't a reasonable workaround, but without this issue the IBO has no intrinsic value. For example, if they fix performance on object creation in ColdFusion 9, the IBO ceases to be useful."
I think this same thing could be said about many design patterns. A design pattern is a proven solution to a problem - but, if you take away the problem, the solution ceases to be useful; this is not IBO specific.
Design patterns cover a wide range of types of solutions from something really specific like the IBO to something *really* generic like Model-View-Controller. At the end of the day, if it's a useful solution to a recurring problem, I don't think it really matters. If you have the problem, the solution is available for your use. I wouldn't use MVC for a headless signal processing application and I wouldn't use Singletons in Ruby. Doesn't mean they aren't both useful when building web apps in ColdFusion.
i do agree with peter when he states its wrong how most people will return an object for a single record and a query result for a collection of records (currently cfwheels does this). i to don't see the benefit in doing this, but understand that it's for performance reasons.
If the underlying architecture is to blame, we may never see the OIP completely done away with, unless they do a major rewrite of CF, so the IBO may be the only lasting solution to the problem.
I also have a clean separation of View and Model so I'm happy.
I began OO with Brians CFC Genearator and although it has been a great help it has also caused OO truble in the sense that I still thought of OO as starting at the db level.
Once you distance yourself from the Service -> DAO / Gaeway view and concentrate on what an object should DO and how it communicates with other objects you start 'getting' OO .. I'm a novice but enjoying the ride!
There is a very good argument for people to provide tried and tested solutions to common Coldfusion problems. The fact that Peter discusses the idea of an IBO is good, although people need to realise when a pattern is appropriate and when it is not. People shouldn't for example start to think that a Coldfusion recordset is a bad data type to use. Sometimes it works perfectly.
I would also like to make one new point too. If there is something that Coldfusion really doesn't do well then find an alternative. Although the solution might involve a different way of approaching the problem in Coldfusion, it could just as easily be to use Java, .Net, the database server, or the server (windows or whatever).
