By Peter Bell

What do you put in your Base Object?

Creating a base object that all of your CFC's extend is a powerful technique, but one you've got to think carefully about . . .

On the one hand, it's great to have some standard methods available to all of your objects. On the other, it lessens portability (your objects now all depend on an inheritance hierarchy starting with your base bean) and it can be a temptation (if you're not careful) to create a “kitchen sink” base object where you throw in a full set of utilities for everything from validation to notifications which is not a very good way to design an app.

Personally, I use a base object to provide for language extensions that I want to have available for all of my CFC's, so I put generic getters and setters into my base bean as well as a dump() method to allow for easy CFDumping within a cfscript block. For CF8 apps I'll also drop in a standard onMissingMethod() for handling getters and setters and other missing methods.

Do you use a custom base object for your applications? If so, what do you put into it, and why?

Comments
I like to add an injector method. It allows me to utilize dependency injection by configuring a array in the superclass with the dependency information.

So I might have a Person class that extends my Base class, and have dependency information in the Person class like so:
variables.dependencies = arraynew(1);
variables.dependencies[1] = structnew();
variables.dependencies[1].name = 'emails';
variables.dependencies[1].class = 'email';
variables.dependencies[1].type = 'onetomany';
// etc with more dependencies if needed

The base class checks for a dependency array when it is instantiated, and if one exists, it runs the injector method. The object service passes itself to the base class when an object is instantiated, so any object can instantiate another object from within itself, to facilitate the injection.
# Posted By Josh Nathanson | 4/14/08 5:38 PM
@Josh, That's an interesting approach, but wouldn't you just let your DI framework take care of that? In my applications I start with a .cfm which then calls lightrbase.cfc which it gets from LighrWire (which I use for DI) and it takes care of injecting all of the dependencies so the bean doesn't have to be aware of them.

Are you using some kind of DI framework (ColdSpring, LightWire, etc.) and what kind of cases do you find putting the dependencies into the bean to fit better for?
# Posted By Peter Bell | 4/14/08 5:44 PM
I have just been playing around with building my own ORM framework for my own usage so I'm not using any DI framework. It seems to make sense to me to configure a dependency from within the parent object. A Person class would literally "contain" its dependencies (email addresses, phone numbers, whatever). It seems to work for all the use cases I've tried so far.

I suppose the downside is that you can't "see" the overall dependencies without cracking open the class file itself - but on the other hand, if you're wondering "what dependencies does the Person class have" you can just open Person.cfc and have a look, without sifting through any XML.
# Posted By Josh Nathanson | 4/14/08 6:29 PM
I'd wonder if this approach would be a little harder to handle mocking out of dependencies for testing and I'd also wonder where you'd keep any configuration information for beans (let's say you had to pass a Datasource to a DAO). Any thoughts on either of those?
# Posted By Peter Bell | 4/14/08 7:13 PM
Well, it's highly experimental. I'm not sure what the ramifications would be for testing. As far as configuration, that basically happens when you instantiate the framework - you pass in the datasource information to a baseDAO.

It's all part of an ORM framework that uses db introspection and builds objects on the fly from the db metadata. My goal was to make it so metadata only exists in the database. So, the database *is* the API, rather than re-describing the metadata in XML or some other way. It's taking DRY to the extreme.

When you instantiate the framework, the datasource metadata is introspected from the db into the application scope. This metadata is then passed in to each object via the object service, for use in setting the object properties and defaults. This way when you add a new property to the Person table for example, such as "birthdate", the new property gets sucked into the object without any additional configuration (of course you have to restart the app so the metadata gets refreshed). It's just "there" for you so when you say person.get('birthdate') it will work.

It probably violates encapsulation, though I've set it up so only properties that exist in the metadata are settable/gettable. In other words, you can only set or get the property "firstname" of the Person class if the field "firstname" exists in the Person metadata, i.e. it exists in the Person table in the database.

The whole framework is four files: baseClass.cfc, baseDAO.cfc, objectService.cfc, and dbintrospect.cfc. Then you build out your specific classes on top of that.

It's all for fun, I wouldn't recommend it for production use at the moment. I'm sort of using it to see what the capabilities of ColdFusion are if you throw out a lot of the rules.
# Posted By Josh Nathanson | 4/14/08 7:55 PM
@Josh, OK< I know exactly where you're going with that. Personally I went in the opposite direction and I generate my db's from my model. I think you'll find as you progress that there are substantial limitations with using a db schema as a source of metadata. For instance, you might have a phone number in a varchar(10). The db doesn't know it is a phone number s it won't validate and transform it correctly. If instead you have a model where a User.Phone is of type phone, you can generate the db schema and rich validations and transformations. Also you may run into problems where the properties you want to expose for an object don't map 1:1 with the columns in the db - canonical example is tbl_User.DateofBirth vs. User.getAge().

That said, if those issues don't map to your problem space, it's certainly a very quick way of generating apps!
# Posted By Peter Bell | 4/15/08 11:55 AM
Peter - yeah, there are some limitations in my method. I was thinking in that situation where there isn't a 1:1 mapping, I can add a method to my custom class. So I can add a getAge() method to my Person class if needed, that takes the birthdate and does the transformation on the data.

As it stands the custom classes are light, containing just a call to super.init(), maybe some dependency configuration, and a few display methods (displayTableRows() for example) so there's plenty of room for any additional methods I may need.

And, I've adopted the IBO methodology for the baseClass so the performance issue of instantiating a bunch of objects is negated.
# Posted By Josh Nathanson | 4/15/08 12:36 PM
Sounds cool!
# Posted By Peter Bell | 4/15/08 12:59 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.