Thursday 18 February 2010

ColdFusion Head First Design Patterns: Strategy

Following on from my previous post on a ColdFusion implementation of the Head First Design Patterns web MVC Pattern is a ColdFusion implementation of the Strategy Pattern.

The Strategy Pattern is probably the most useful pattern, to quote the Head First book:

"The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it."

Abstract Duck

Duck.cfc

<cfcomponent output="true">

  <cffunction access="public" returntype="void" name="display">    
  </cffunction>
  
  <cffunction access="public" returntype="void" name="performFly">
    <cfset VARIABLES.flyBehaviour.fly()>    
  </cffunction>
  
  <cffunction access="public" returntype="void" name="performQuack">
    <cfset VARIABLES.quackBehaviour.quack()>    
  </cffunction>

  <cffunction access="public" returntype="void" name="swim">
    <cfoutput>All ducks float, even decoys!<br/></cfoutput>    
  </cffunction>

  <cffunction access="public" returntype="void" name="setFlyBehaviour">
    <cfargument required="true" type="FlyBehaviour" name="flyBehaviour">
    <cfset VARIABLES.flyBehaviour = ARGUMENTS.flyBehaviour>
  </cffunction>

  <cffunction access="public" returntype="void" name="setQuackBehaviour">
    <cfargument required="true" type="QuackBehaviour" name="quackBehaviour">
    <cfset VARIABLES.quackBehaviour = ARGUMENTS.quackBehaviour>
  </cffunction>
    
</cfcomponent>

Quack Strategies

Quack.cfc

<cfcomponent implements="QuackBehaviour" output="true">

  <cffunction access="public" returntype="Quack" name="init">
    <cfreturn THIS>
  </cffunction>

  <cffunction access="public" returntype="void" name="quack">
    <cfoutput>Quack<br/></cfoutput>  
  </cffunction>
  
</cfcomponent>

Fly Strategies

FlyWithWings.cfc

<cfcomponent implements="FlyBehaviour" output="true">
  
  <cffunction access="public" returntype="FlyWithWings" name="init">
    <cfreturn THIS>
  </cffunction>   
  
  <cffunction access="public" returntype="void" name="fly">
    <cfoutput>I'm flying!<br/></cfoutput>
  </cffunction>
  
</cfcomponent>

FlyNoWay.cfc

<cfcomponent implements="FlyBehaviour" output="true">
  
  <cffunction access="public" returntype="FlyNoWay" name="init">
    <cfreturn THIS>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="fly">
    <cfoutput>I can't fly<br/></cfoutput>
  </cffunction>
  
</cfcomponent>

FlyRocketPowered.cfc

<cfcomponent implements="FlyBehaviour" output="true">
  
  <cffunction access="public" returntype="FlyRocketPowered" name="init">
    <cfreturn THIS>
  </cffunction>  
  
  <cffunction access="public" returntype="void" name="fly">
    <cfoutput>I'm flying with a rocket!<br/></cfoutput>
  </cffunction>
  
</cfcomponent>

Duck Implementations

MallardDuck.cfc

<cfcomponent extends="Duck" output="true">

  <cffunction access="public" returntype="MallardDuck" name="init">
    <cfset VARIABLES.flyBehaviour = createObject("component", "FlyWithWings").init()>
    <cfset VARIABLES.quackBehaviour = createObject("component", "Quack").init()>
    <cfreturn THIS>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="display">
    <cfoutput>I'm a real Mallard duck<br/></cfoutput>
  </cffunction>
   
</cfcomponent>

ModelDuck.cfc

<cfcomponent extends="Duck" output="true">

  <cffunction access="public" returntype="ModelDuck" name="init">
    <cfset VARIABLES.flyBehaviour = createObject("component", "FlyNoWay").init()>
    <cfset VARIABLES.quackBehaviour = createObject("component", "Quack").init()>
    <cfreturn THIS>
  </cffunction>
  
  <cffunction access="public" returntype="void" name="display">
    <cfoutput>I'm a model duck<br/></cfoutput>
  </cffunction>
   
</cfcomponent>

Test Page

MiniDuckSimulator.cfm

<cfset mallard = createObject("component", "MallardDuck").init()>
<cfset mallard.performQuack()>
<cfset mallard.performFly()>

<cfset model = createObject("component", "ModelDuck").init()>
<cfset model.performFly()>
<cfset model.setFlyBehaviour(createObject("component", "FlyRocketPowered").init())>
<cfset model.performFly()>

Source Code