Raymond Camden's Blog Rss

Baby steps in Factory Land (right over the edge of a cliff...)

14

Posted in Development, ColdFusion | Posted on 02-22-2007 | 3,067 views

So I was really enjoying object factories. They seemed so clean. So cool. So... perfect for helping my applications get a bit simpler to manage. I decided as my first trial attempt at using them I'd work with Galleon. Rob Gonda had already done the work for me - I just needed to go over it and ensure it worked with my latest build. (Rob had sent me the files eons ago.)

For those who haven't looked at Galleon, it is a bit complex. The CFCs include: conference, forum, galleon, message, rank, thread, user, utils. To make matters worse, each of these CFCs needed instances of almost every single other one. message.cfc is a good example. It actually used thread, forum, conference, utils, utils, galleon. Messy. But hey it worked.

So I switched to a simple object factory (again, using Rob's code), where each object was created and had the dependent objects passed in to the constructors. (This is called constructor injection.) Everything worked fine until I reloaded and bam.

CPU shot up to 100%.

RAM usage for jrun.exe went up past 500k.

Life as we know it came to an end.

(Ok that last part may not be accurate.)

Turns out constructor injection has an issue with circular dependencies (see Peter Bell's good article on constructor/setter injection).

Now smarter fellows then me made it clear that this wasn't necessarily an issue with factories but in how I implemented it. Turned out I had runtime logic that went like so:

view plain print about
1Make a message CFC.
2Ok, but I need a forum as well.
3Make a forum CFC.
4Ok, but I need a thread as well.
5Make a thread CFC.
6Ok, but I need a message.

And in each case I kept making new objects. All in all this was a big huge mess. Let me describe one scenario in general - adding a message. The logic went like so: (and this is actually a simpler version of the real thing)

view plain print about
1Add Message
2
3Get a thread record for the thread that owns this forum, see if read only
4
5If not, add to DB.
6
7Now lets send an email.
8
9I want the email to include thread/forum/conference, so ask each of these CFCs for an instance of the right database records so that it can add a nice label to the email.

There is definitely a need for a rewrite here. So what to do? Derek Perez (and others) suggested moving to a service layer. Instead of my code speaking to Message.cfc for example, it would speak to a MessageService. Let's rewrite the process above using services and see how it could change.

view plain print about
1messageService.addMsg()
2
3First, call the threadService and see if this thread is read only. No? Cool, go on.
4
5Add the record to database. This is done by Message.cfc. The service orders this.
6
7Now I need to send email. I know I need my nice labels. I'll ask my Thread, Forum, and Conference Services to simply return the labels for these values. Each of those services does - does what? I don't care (I being the Message Service) - they just handle it.

So I went into all of this looking to make it simpler to create CFCs. I had the problem of: CFC A has N arguments, and I make CFC A in multiple places. When the API changes, I'm all of a sudden screwed if I don't fix all the createObjects.

I think I can still make use of the objectFactory. Perhaps my Services will call the factory. Since I won't have all the circular dependencies anymore, it may not be necessary.

So what's the plan? Right now I screwed up. I updated Galleon, thought I had tested well, but missed it. So I actually removed the download from RIAForge for now. Obviously that isn't great. So I need to rewrite a good chunk of code I believe. If folks want to help, it would be appreciated - but to be honest I want to at least start this myself. I'm the kind of person who doesn't learn if I don't write it myself, and boy was this a big learning experience.

Comments?

Comments

[Add Comment] [Subscribe to Comments]

At first glance, it seems you are, perhaps, just doing too much in Message, Forum, and Thread as object types. Sounds like perhaps you need to separate out the data "objects" from what I might call the "managers" (or even, gasp, "services"). Those with deeper skills than I will have more formalized vocabulary, but I have found that when you have circular dependencies like that it's a signal that your underlying decisions about which CFCs do what might need to be revisited.
This is somewhat was I was referring to in one of the other posts and a concern I had. Good Luck :)
Two words: ColdSpring & cfcUnit.

I understand that making Galleon depend on ColdSpring is problematic for distribution purposes but adopting cfcUnit and creating a large suite of unit tests would probably catch errors / bugs before they got onto RIA Forge :)

Keep up the good posts - we all learn from your documented learning process. Thanx!
Wow, I also thought I tested the code... I can send you an updated version using setters instead of constructors so it works with circular dependencies. It's really not such a big change.
Don't be discouraged in the world of OOP! I think that a service layer would be a very intelligent move for this application. Abstracting away any knowledge of the objects to only the service layer is a great way to minimize dependencies because only the service implementation should have knowledge of the bits and pieces involved in the general task you wish to accomplish. This is inevitably one of the best reasons to utilize supporting frameworks such as coldspring, but if you can't, I would definitely recommend trying to implement at leastservice layers into your code to handle tying together your model (much like you would use a controller). Just my humble 2 cents :) Remember, if you ever need a hand, I am always pingable!
Sent
@Nathan: Absolutely. Galleon was an "old school" CFC app for me, soon after I had done BlogCFC. BlogCFC had one giant CFC. Galleon was "better" as I had broken up the CFCs a bit. But it isn't what I do now, which is typically Bean/DAO/Gateway.

@Sean: Amen. :) I don't want to go the CS route yet. I want to make sure I truly get this. But that is no excuse not to use cfcUnit.

@Rob - you code did work. I had to modify one thing - and I forget now where it was - but at that point it broke and things just got worse. Totally my fault. :)

@Derek: Not discouraged at all - despite the title. ;)
Rob is right. Just move some or all of your injection to setter based injection and you'll be fine.

You could also do "psuedo constructor injection" (basically setter injection using the init() method as one big setter by calling it after creating all of the beans). Doesn't work for use cases where you call methods on dependent objects in the init() of other objects so it would be a hack for today rather than a complete solution.

Also, if you want to see how to check for circular constructor dependencies, I wrote a very simple recursive function in LightWire that does all the heavy lifting - it's the getDependentObjectList() method in LightWire.cfc at http://svn.riaforge.org/lightwire. You'd have to change to tie it into your system, but it's pretty easy to use.
@Peter - Turns out I had made a dumb mistake. I mean a dumb mistake besides my original clunky CFCs. ;) Rob fixed me and explained it to me. I would still like to move to a service layer - but I'm going to go with his fix for now mainly to get the app back online. I'll blog what I had done wrong and how Rob had fixed it.

Like most things - the second you see the fix it becomes obvious.
I understand your desire to learn the concept before you dive into ColdSpring. I would give it a shot though, learning ColdSpring actually introduced the concept of object factories to me and made it easier to understand. I figured out how easy yet powerful it was and I have been hooked ever since!
@Ray, Glad I'm not the only one who seems to specialize in dumb mistakes!
Ray, if you decide to use cfcUnit on this or any future projects, will you blog your progress? I've been wanting to start using it, but haven't had the time or seen much in the way of examples/tutorials to get me going, and your "learning" posts are great for stuff like that.
@Ken, I only just noticed that the cfcUnit website doesn't have the most up-to-date version of the documentation posted. However, if you download the cfcUnit package, it does include the most up-to-date documentation - which has quite a bit of information about why / how to do unit testing as well as quite a few examples.

If anyone wants to volunteer to help Paul Kenney with the cfcUnit website, I know he'd appreciate it!
I just put the latest cfcUnit docs up on my site:

http://corfield.org/cfcunit/documentation/

[Add Comment] [Subscribe to Comments]