Welcome!

ColdFusion Authors: Maureen O'Gara, Hovhannes Avoyan, Yakov Fain, Pat Romanski, Liz McMillan

Related Topics: ColdFusion

ColdFusion: Article

Mixins

Writing software that's more flexible and maintainable

The only thing needed is to include Teacher.cfm and write an init method - a sort of constructor for our CFC.

Our StudentTeacher.cfc is similar:

<cfcomponent displayname="StudentTeacher" extends="Student">
    <cfinclude template="Teacher.cfm" />

    <cffunction name="init" access="public" output="false">
      <cfargument name="average" required="true" />
      <cfargument name="name" required="true" />
      <cfargument name="test" required="false" default=null />
      <cfset super.init(arguments.average, arguments.name) />
      <cfset set('test', arguments.test) />
      <cfreturn this />
    </cffunction>
</cfcomponent>

Without extending or implementing anything, we have the one authoritative, unambiguous representation of what a teacher is.

That's not to say, though, that we can't extend classes. In fact, StudentTeacher extends Student.cfc:

<cfcomponent displayname="Student" extends="BaseComponent">
    <cfinclude template="Student.cfm" />

    <cffunction name="init" access="public" output="false">
      <cfargument name="average" required="true" />
      <cfargument name="name" required="true" />
      <cfset set('average', arguments.average) />
      <cfset set('name', arguments.name) />
      <cfreturn this />
    </cffunction>
</cfcomponent>

Student is similar to Teacher and includes the one authoritative, unambiguous representation of what a student is: Student.cfm:

<cfset variables.instance.average = 0 />
<cfset variables.instance.name = null />

<cffunction name="study" access="public" output="false">
    <cfreturn "Yes, yes, I'm studying..." />
</cffunction>

<cffunction name="takeTest" access="public" output="false">
    <cfargument name="test" required="true" />
    <cfif arguments.test.get('percentOfGrade') LT 10 AND get('average') GTE 100>
      <cfreturn skipTest() />
    <cfelse>
      <cfreturn workHardOnTest(arguments.test) />
    </cfif>
</cffunction>

<cffunction name="skipTest" access="private" output="false">
    <cftrace type="Information" text="I think I'll just skip this test.
    I've already got a #get('average')#" />
</cffunction>

<cffunction name="workHardOnTest" access="private" output="false">
    <cfargument name="test" required="true" />
    <cftrace type="Information" text="I'm taking the test: #arguments.test.get('name')#" />
</cffunction>

We should make sure all this works:

<cfset anne = CreateObject('component', 'Student').init(94, 'Anne Baker') />
<cfset bob = CreateObject('component', 'Student').init(82, 'Bob Carter') />

<cfset carla = CreateObject('component', 'StudentTeacher').init(100, 'Carla Davis') />

<cfset test = CreateObject('component', 'Test').init('OO Development with ColdFusion', 8) />

<!--- have the Teacher do its thing--->
<cfset teacher = CreateObject('component', 'Teacher').init(test) />
<cfset teacher.addStudent(anne) />
<cfset teacher.addStudent(bob) />
<cfset teacher.addStudent(carla) />

<cfoutput>
     #teacher.assignHomework()#<br>
     #teacher.giveTest()#
</cfoutput>

<!--- now let's have the StudentTeacher --->
<cfset carla.set('test', test) />
<cfset carla.addStudent(anne) />
<cfset carla.addStudent(bob) />

<cfoutput>
     #carla.assignHomework()#<br>
     #carla.giveTest()#
</cfoutput>

Though it's not important for this discussion, I've included the code for Test.cfc for completeness. It can be found in Listing 1.

Object-Based Mixins
Class-based mixins work well when we want to build mixins into our design. As their name suggests, class-based mixins affect all instances of the class. Sometimes, though, that's not what we want. We may have objects in memory that we need to make structural changes to; we may need to affect some, but not all, members of a class; or we may be using code that we have no ownership of and no ability to alter.

In any of these cases (and more), a class-based mixin is not appropriate. Object-based mixins provide the ultimate in flexibility - even during runtime. Object-based mixins let us "inject" all methods and variables from one object (not class) into another object.

More Stories By Hal Helms

Hal Helms is a well-known speaker/writer/strategist on software development issues. He holds training sessions on Java, ColdFusion, and software development processes. He authors a popular monthly newsletter series. For more information, contact him at hal (at) halhelms.com or see his website, www.halhelms.com.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.