An object generally knows only about itself, the data contained in its own variables scope. In order for an object to know about external data, it needs a way to "see" it. Passing data into an object, often only when it is created, is called data injection.

Crossing the border

In order for an Object to know about external values (either from a CFM or from another CFC), data needs to be injected into it. Anything placed in the object's variables scope exists for the life of the object and is available to every function inside it.

Object.cfc with data injection
view plain print about
1<cfcomponent output="false" name="Object" hint="Simple CF Object">
2
3    <cffunction name="init" access="public" output="false" returntype="Object" hint="constructor for Object.cfc">
4        <cfargument name="foo" type="numeric" required="true" default="0" hint="Some number" />
5        <cfset variables.foo = arguments.foo />
6        <cfreturn this />
7    </cffunction>
8
9    <cffunction name="getVariablesScope" access="public" output="false" returntype="struct">
10        <cfreturn variables />
11    </cffunction>
12
13    <cffunction name="getFoo" access="public" output="false" returntype="string">
14        <cfreturn variables.foo />
15    </cffunction>
16
17</cfcomponent>

Now let's pass in a value for "foo" from the CFM page.

object_test.cfm
view plain print about
1<cfset variables.foo = 5 />
2<cfset variables.obj = createObject("component", "Object").init( foo = variables.foo ) />
3<cfdump var="#variables.obj.getVariablesScope()#" />
struct
FOO5
GETVARIABLESSCOPE
function getVariablesScope
Arguments:none
Return Type:struct
Roles:
Access:public
Output:false
INIT
function init
Arguments:
NameRequiredTypeDefault
fooRequirednumeric 0
Return Type:Object
Roles:
Access:public
Output:false
THIS
componentObject
INIT
function init
Arguments:
NameRequiredTypeDefault
fooRequirednumeric 0
Return Type:Object
Roles:
Access:public
Output:false
GETVARIABLESSCOPE
function getVariablesScope
Arguments:none
Return Type:struct
Roles:
Access:public
Output:false

At this point, "foo" exists in the variables scope of the CFM file, while another distinct variable named "foo" exists in the variables scope of the CFC (object).

Changing variables.foo outside of the object
view plain print about
1<cfset variables.foo = 4 />
2
3<cfset obj = createObject("component", "Object").init( foo = variables.foo ) />
4
5<cfoutput>
6    1. variables.foo: #variables.foo#<br />
7    2. obj.getFoo(): #obj.getFoo()#<br />
8</cfoutput>
9
10<cfset variables.foo = 6 />
11<cfoutput>
12    3. variables.foo: #variables.foo#<br />
13    4. obj.getFoo(): #obj.getFoo()#
14</cfoutput>
  1. variables.foo: 4
  2. obj.getFoo(): 4
  3. variables.foo: 6
  4. obj.getFoo(): 4

Since the variables scope of the CFM and the variables scope of the CFC don't know about each other. Changing the value of "variables.foo" outside of the object does not change the value of "variables.foo" inside the object.

The same is true when we inject the value of a session variable into the object.

Changing session.foo outside of the object
  1. session.foo: 1
  2. obj.getFoo(): 1
  3. session.foo: 3
  4. obj.getFoo(): 1

Let's add a function to Object.cfc that adds 4 to the value of variables.foo:

view plain print about
1<cffunction name="addFourToFoo" access="public" output="false" returntype="void">
2    <cfset variables.foo = variables.foo + 4 />
3</cffunction>

Now what happens to a variable external to the object when we change the value inside the object?

Changing variables.foo inside the object
  1. session.foo: 1
  2. obj.getFoo(): 1
  3. session.foo: 1
  4. obj.getFoo(): 5

The value of variables.foo inside the object was originally defined by the value of session.foo. When the value inside the object is changed, session.foo is unaffected. This sounds perfectly reasonable, so why does this bear mentioning?

So far, we've only been talking about passing simple data values into an object. Later we'll talk about passing objects as arguments in order to build more complex objects. When we talk about object composition, we'll re-visit session variables and objects.

Best Practices

For many types of objects, it's best to only set data into the variables scope through the constructor when the object is created. This is especially true when those objects are placed into the application, session or server scopes.

However, data can be passed into an object through any public or remote function. Those functions can then place that data into the object's variables scope. Doing so after an object has been created has its pros and cons, but these vary depending on what type of object you're using.

Why insulate objects?

An object has a task or set of tasks and often shouldn't know about the outside world.

Much like Milton from Office Space, if an object can gain access to another object it shouldn't know about (like a check for millions of dollars) or can change the value of shared scope variable at any time (i.e. server.officeIsOnFire = true), it may end up burning your application to the ground and retiring to Jamaica.

What's next?

We've talked about many different variable scopes:

  1. server
  2. application
  3. session
  4. variables (outside an object)
  5. variables (inside an object)

Before we get into specific types of objects, we have to cover the var scope and why it is the most important and most overlooked scope in object oriented development with Coldfusion.