What are "Beans," anyhow?

Beans are a hot topic amongst the Object-Oriented ColdFusion crowd. For a while, the ColdFusion-scoped definition seemed to be changing every few months, but it looks like we're settling down a bit now that tools like ColdSpring are showing us just how abstract the concept of a ColdFusion "Bean" is.

[Read more on Joe Rinehart's blog]

In short, my humble opinion is that any CFC is a "Bean" as long as it follows two simple rules:

  1. All member variables are kept private, living in the variables scope.
  2. Members are accessed only through methods named "get[MemberName]" and "set[MemberName]."

That's it.

What's nice about this loose of a definition is that it doesn't mean that only CFCs like Contact with Get/SetFirstname() and Get/SetLastname() are beans. Many things can be beans: service facades, Model-Glue Controllers, Mach-II Listeners, and even DAOs can act as beans.

A Simple Example

"How's this," you say? "Isn't a Bean just like a structure on steroids, good for representing a database table?"

"No," I say, "a Bean is any CFC that implements the two rules: private member variables accessed through Get/Set pairs."

"Why's this cool," you ask?

It provides a convention through which things like frameworks can rely upon to make your life easier.

For one, it lets Mach-II and Model-Glue developers utilize "Event Beans" to collect user-entered data. If you used something that didn't act like a bean, and instead used the Set/GetValue() style to collect a Contact's information, you'd have code in a Model-Glue Controller looking a lot like this:

<cfset contact = createObject("component", "myapp.model.Contact").init() />
<cfset contact.setValue("firstname", arguments.event.getValue("firstname")) />
<cfset contact.setValue("lastname", arguments.event.getValue("lastname")) />
<cfset contact.setValue("address", arguments.event.getValue("address")) />
<cfset contact.setValue("city", arguments.event.getValue("city")) />
<cfset contact.setValue("state", arguments.event.getValue("state")) />
<cfset contact.setValue("zip", arguments.event.getValue("zip")) />

That gets ugly if you've got a lot of data.

Instead, if we force Contact to conform to the Bean pattern, Model-Glue/Mach-II can predict the names of specific Get/Set methods, see if those methods exist, and try to automatically populate your bean. In Model-Glue, we'd replace the above code with this:

<cfset contact = arguments.event.makeEventBean("myapp.model.Contact") />

A lot easier!

Getting More Complex: Beans, Model-Glue, ColdSpring, and "Autowiring"

For something more complex, let's examine the typical Model-Glue constructor for managing a table called SomeTable. It'd probably have a constructor a lot like this:

<!--- Some attributes omitted for clarity --->
<cffunction name="Init">
<cfargument name="ModelGlue" />
<cfargument name="InstanceName" />
<cfset var datasource = "" />

<!--- Init the base ModelGlue.Core.Controller we extend --->
<cfset super.Init(arguments.ModelGlue) />

<!--- Set up DAO and Gateway Factories --->
datasource = getModelGlue().getConfigBean("datasource") />

<cfset variables._daoFactory = createObject("component", "myapp.model.data.daoFactory").init(datasource) />
<cfset variables._gatewayFactory = createObject("component", "myapp.model.data.gatewayFactory").init(datasource) />

<cfreturn this />

That's some ugly, repetitive code that you have to add to each controller.

If we decide that, instead of building the DAO and Gateway factories manually in the controller, we'll allow them to be set Bean-style (through methods named setDAOFactory() and setGatewayFactory() ), we have an understood contract through which something else can do the work for us.

In Model-Glue, that something else is ColdSpring; Model-Glue's ColdSpring adapter knows to look for methods on your Controller named set[BeanId/Type] and then to "autowire" appropriately named/typed beans it knows about in the ColdSpring.xml file.

Again, by relying on the Get/Set convention that's the heart of the ColdFusion "Bean" pattern, a framework can do a lot of legwork for us.

So, we can ditch the constructor altogether, and simply add two methods:

<cffunction name="setDAOFactory">
<cfargument name="DAOFactory" />
<cfset variables._daoFactory = arguments.daoFactory />

<cffunction name="setGatewayFactory">
<cfargument name="GatewayFactory" />
<cfset variables._gatewayFactory = arguments.gatewayFactory/>

Now, when Model-Glue loads your controller, it'll hand the Controller instance to ColdSpring, and ColdSpring will collect all methods named set[something], and if it contains a definition for a bean (see, there it is again!) called [something], it'll call the set[something] method.

In English, I'd set up the DAOFactory in the Coldspring XML file, then Model-Glue would automagically "autowire" the DAOFactory to the Controller the moment Model-Glue starts.


Following the convention of get/set pairs means some extra typing up front, but with tools like Model-Glue, Mach-II, and ColdSpring all providing utilities that leverage this convention, the time saved writing and maintaining application code is well worth it!

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
© 2018 Joe Rinehart
BlogCFC was created by Raymond Camden. This blog is running version