Inheritance


Just as we can use the word "my" to refer to a CFC's THIS scope ("my ID is 123 and my First Name is Fred…"), in inheritance, we can think of the words is a. An actor is a person. A cat is a mammal. A novel is a book. In these cases, actor, cat, and novel are "children" of person, mammal, and book. Some parents can exist by themselves; there can be a person who is not an actor. Some other parents, though, are abstract; you will never see a creature in the zoo called, simply, "mammal." Rather, the parent is intended as more of a handy template upon which more specific things can be based. Although two different mammals share some characteristics by virtue of being mammals (they are warm-blooded and give birth to live young, for example), they can be made different either by having unique properties and behaviors, or by modifying some aspect of the parent (consider the egg-laying platypus!).

Our list of known facts in the movie studio application included the statements that actors and directors are both types of people with some common properties and some unique. So by being types of "people," you could create a component to represent a "person" and have each of these variants inherit from it.

Listing 19.11 is the basic person component. It has a first name and last name (stored in the THIS scope) and has one function that "shows" the person by outputting the first and last name.

Listing 19.11. person.cfcThe Basic person Component
 <!---   person.cfc   Component that contains a "person", firstname and lastname only   Modified by Ken Fricklas (kenf@fricklas.com)   Modified: 2/15/2005   Code from Listing 19.11 ---> <cfcomponent displayName="Person" hint="Parent Component - Person"> <cfparam name="THIS.firstName" default="John"> <cfparam name="THIS.lastName" default="Doe"> <cffunction name="showPerson" output="true">   <B>#THIS.firstName# #THIS.lastName#</B> </cffunction> </cfcomponent> 

A component inherits from a parent component with the EXTENDS attribute of the <cfcomponent> tag. The value of the attribute is the name of the component upon which the new component should be based. Thus, a director component could consist of nothing more than this code:

 <cfcomponent displayName="Movie Director" extends="person"> </cfcomponent> 

Now, the director is an exact copy of the person and has "inherited" all the properties and methods of its parent. A CFML page, then, could create an instance of the director and invoke the methods of the person component as though they were part of the director component:

 <cfobject component="director" name="myDirector"> <cfoutput>#myDirector.showPerson()#</cfoutput> 

Just because the parent says something doesn't mean the child is stuck with it (they wish, anyway). In components, at least, that is truly the case. The component can "override" parts of the parent component. If we added code to the director component to set the THIS scope with variables of the same name, the director, because it's the one being invoked, will take precedence. So the director component is now coded like this:

 <<cfcomponent displayName="Movie Director" extends="person">   <cfset THIS.firstName = "Jim">   <cfset THIS.lastName = "Jarofmush"> </cfcomponent> 

and when invoked from the CFML page will output "Jim Jarofmush" instead of "John Doe" as it did before. The THIS scope assignments made in the child overrode those of the parent. Likewise, adding a showPerson function to the director component will override the showPerson function from the parent:

 <cffunction name="showPerson" output="true">   <B>A swell director named #THIS.firstName# #THIS.lastName#</B> </cffunction> 

In addition to the child's being able to invoke functions that are really part of the parent component (and overriding them, if desired), the parent can call functions that are part of the child by referencing them in the instance's THIS scope. Say we added a function called showDetail to the director component:

 <cfset THIS.credits = arrayNew(1)> <cfset THIS.credits[1] = "The Phantom Dentist"> <cfset THIS.credits[2] = "Austin Showers"> <cfset THIS.credits[3] = "Men in Slacks II"> <cffunction name="showDetail" output="true">   Credits include:   <UL>   <cfloop index="i" from="1" to="#arrayLen(THIS.credits)#">     <LI>#THIS.credits[i]#</LI>   </cfloop>   </UL> </cffunction> 

We could then modify the showPerson function in the parent to refer to the child's showDetail function by prefacing the name of the function to be called with the THIS scope. This code means, "Invoke the showDetail method on the instance of the component that I am currently in…":

 <cffunction name="showPerson" output="true">   <cfargument name="showDetail" required="false" type="boolean"       default="false">   <B>#THIS.firstName# #THIS.lastName#</B>   <cfif showDetail>     <BR>#THIS.showDetail()#   </cfif>  </cffunction> 

In the calling template now, the showPerson function can take an optional Boolean argument which, if present, will invoke the showDetail method from the director component:

 <cfobject component="director" name="myDirector"> <cfoutput>#myDirector.showPerson(true)#</cfoutput> 

CAUTION

Notice in the preceding example that the method was invokedincluding the passing in of argumentsin function syntax. The showPerson method took one Boolean argument which we were able to pass in like so: showPerson(true). This is just another way of invoking component methods. Bear in mind, though, if you use this approach, order is important! The order in which you pass arguments with the function must match the order in which the <cfargument> tags are coded!


The result of our showPerson function now will be a combination of showPerson from the person component and showDetail from the director component:

Jim Jarofmush

Credits include: The Phantom Dentist Austin Showers Men in Slacks II

This technique can be useful when multiple components are descendants of the same parent but require slightly different methods. Say we based an actor component on the same "person" parent using this code:

 <cfcomponent displayName="Movie Actor" extends="person">   <cfset THIS.firstName = "Judi">   <cfset THIS.lastName = "Dents"> <cffunction name="showDetail" output="true">   Star of the hit <EM>The Importance of Being Sternest</EM>. </cffunction> </cfcomponent> 

When the showPerson function is invoked, the appropriate showDetail function is used, depending on whether the component is an actor or director:

 <!--- instantiate an actor and a director ---> <cfobject component="director" NAME="myDirector"> <cfobject component="actor" NAME="myActor"> <!--- demonstrate that showPerson calls different showDetail functions for each ---> <cfoutput>   Directed by:<br>     #myDirector.showPerson(true)#   <br>   Starring:<br>     #myActor.showPerson(true)# </cfoutput> 

This code results in this output:

Directed by:

A swell director named Jim Jarofmush

Starring:

Judi Dents

Star of the hit The Importance of Being Sternest.

Must you use inheritance in your ColdFusion applications? Certainly not. But it can be very useful in ways similar to other code-reuse techniques. Components can be built-in "branches," as in a family tree with chains of ancestral parent components, each providing base functionality to their children.

Component packages can help with this type of organization, too, to make applications more easily maintained. In that case, the extend="..." attribute uses the same package path syntax as a <cfinvoke> tag. For example, to inherit from a component called "person" in the package myApp.components, the <cfcomponent> tag would be coded like this:

 <cfcomponent extends="myApp.components.person"> 



Advanced Macromedia ColdFusion MX 7 Application Development
Advanced Macromedia ColdFusion MX 7 Application Development
ISBN: 0321292693
EAN: 2147483647
Year: 2006
Pages: 240
Authors: Ben Forta, et al

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net