
The basic data object concept
Now we create the function for assigning the encapsulated variables of the object when it is created. We are now ready to write the next version of the calling page. We are only going to change two things on that page. We will change the line where we were creating a second recordset to a call to the method for performing the assigning of the internal variable values in the object. Then we will change our content section of the code to use the object getters to pull the encapsulated variable values. The highlighted rows are the ones changed.
<!--- Example: 2_6.cfm ---> <!--- Processing ---> <cfparam name="url.id" default="0"> <cfscript> objProduct = createObject("component","product_2").init(); rsProducts = objProduct.getRecordset(); objProduct.load(url.id); </cfscript> <!--- Content ---> <ul><cfoutput query="rsProducts"> <li><a href="?id=#rsProducts.id#">#rsProducts.name#</a> </li></cfoutput> </ul> <cfif objProduct.get_name() NEQ ""> <cfoutput> <table> <tr> <th>Product</th> <td>#objProduct.get_name()#</td> </tr> <tr> <th>Description</th> <td>#objProduct.get_description()#</td> </tr> <tr> <th>Price</th> <td>#dollarFormat(objProduct.get_price())#</td> </tr> </table> </cfoutput> </cfif>
What we are doing is very good for making it easier and easier to separate processing from markup. We are using the same object to do our processing and returning data for displaying our output on the markup content section of the page. This is a great way to reuse things over and over.
One of the goals of objects is to maximize code reuse. What we are about to do is known as "refactoring" our object. We are going to take what we have and make it simpler. Right now, we have a bunch of getters and setters. Every new data-focused object is going to have unique getters and setters. What this means is that the interface on this object is not very portable. What will we do when we build our shopping cart? The item object will have a different interface than the product item. It is not always possible to match the interfaces so they have common methods and arguments. Yet, in pragmatic terms it is usually better to do things that way.
Let's take our getters and setters and move them all to one getter and one setter. The getter can retrieve all the hidden attributes and the setter would be able to do the same thing. Since we are not setting any values at this time, we will leave those as they are until later. The principle is the same. We will remove all of the getter functions and save the new CFC as product_3.cfm
for this exercise. Add the new get()
method to the top of the getter and setter section. We can also see in our code that we use arguments.attribute
to check if a variable exists and if so pull it. If a false field is requested, then we return an empty field in this case, rather than an error.
<!--- Getter and Setter Methods ---> <cffunction name="get" access="public" output="false"> <cfargument name="attribute" required="true"> <cfset var myReturn = ""> <cfif structKeyExists(variables.attributes,arguments.attribute)> <cfreturn variables.attributes[arguments.attribute] /> <cfelse> <cfreturn ""> </cfif> </cffunction>
Now we need to modify or create another version of the calling page. Don't forget that we just created a new object, so we need to make sure the calling page is creating an instance of the correct object class. Change that class to product_3
now. Then change the variable methods from the current getters such as #objProduct.get_name()#
to code like #objProduct.get('name')#
. The difference seems to be minor. Yet the portability of this object has just increased. We are still able to have protected variables, but we now have much more versatile object classes. Again, this is just the start of how much power we can build in. Hey, we have to start somewhere before we can arrive at any destination, right?
<!--- Example: 2_7.cfm ---> <!--- Processing ---> <cfparam name="url.id" default="0"> <cfscript> objProduct = createObject("component","product_3").init(); rsProducts = objProduct.getRecordset(); objProduct.load(url.id); </cfscript> <!--- Content ---> <ul><cfoutput query="rsProducts"> <li> <a href="?id=#rsProducts.id#">#rsProducts.name#</a> </li></cfoutput> </ul> <cfif objProduct.get('name') NEQ ""> <cfoutput> <table> <tr> <th>Product</th> <td>#objProduct.get('name')#</td> </tr> <tr> <th>Description</th> <td>#objProduct.get('description')#</td> </tr> <tr> <th>Price</th> <td>#dollarFormat(objProduct.get('price'))#</td> </tr> </table> </cfoutput> </cfif>
Now we have achieved about as simple an object as we need to show different aspects of working with objects. Let's deal with a couple of things that we have not talked about to wrap things up.