Continuing in this design patterns series taken from Head First Design Patterns, is a ColdFusion implementation of the Observer Pattern.
Interfaces
Subject.cfc
<cfinterface> <cffunction access="public" returntype="void" name="registerObserver"> <cfargument required="true" type="Observer" name="o"> </cffunction> <cffunction access="public" returntype="void" name="removeObserver"> <cfargument required="true" type="Observer" name="o"> </cffunction> <cffunction access="public" returntype="void" name="notifyObservers"> </cffunction> </cfinterface>
Observer.cfc
<cfinterface> <cffunction access="public" returntype="void" name="update"> <cfargument required="true" type="numeric" name="temperature"> <cfargument required="true" type="numeric" name="humidity"> <cfargument required="true" type="numeric" name="pressure"> </cffunction> </cfinterface>
DisplayElement.cfc
<cfinterface> <cffunction access="public" returntype="void" name="display"> </cffunction> </cfinterface>
Subject Implementation
WeatherData.cfc
<cfcomponent implements="Subject" output="false"> <cffunction access="public" returntype="Subject" name="init"> <cfset VARIABLES.observers = arrayNew(1)> <cfreturn THIS> </cffunction> <cffunction access="public" returntype="void" name="registerObserver"> <cfargument required="yes" type="Observer" name="o" /> <cfset arrayAppend(VARIABLES.observers, ARGUMENTS.o)> </cffunction> <cffunction access="public" returntype="void" name="removeObserver"> <cfargument required="yes" type="Observer" name="o" /> <cfset arrayDelete(VARIABLES.observers, ARGUMENTS.o)> </cffunction> <cffunction access="public" returntype="void" name="notifyObservers"> <cfset var LOCAL = {}> <cfloop index="LOCAL.o" array="#VARIABLES.observers#"> <cfset LOCAL.o.update(VARIABLES.temperature, VARIABLES.humidity, VARIABLES.pressure)> </cfloop> </cffunction> <cffunction access="public" returntype="void" name="measurementsChanged"> <cfset notifyObservers()> </cffunction> <cffunction access="public" returntype="void" name="setMeasurements"> <cfargument required="yes" type="numeric" name="temperature" /> <cfargument required="yes" type="numeric" name="humidity" /> <cfargument required="yes" type="numeric" name="pressure" /> <cfset VARIABLES.temperature = ARGUMENTS.temperature> <cfset VARIABLES.humidity = ARGUMENTS.humidity> <cfset VARIABLES.pressure = ARGUMENTS.pressure> <cfset measurementsChanged()> </cffunction> </cfcomponent>
Display Elements
CurrentConditionsDisplay.cfc
<cfcomponent implements="Observer, DisplayElement" output="true"> <cffunction access="public" returntype="CurrentConditionsDisplay" name="init"> <cfargument required="true" type="WeatherData" name="weatherData"> <cfset VARIABLES.weatherData = ARGUMENTS.weatherData> <cfset VARIABLES.weatherData.registerObserver(THIS)> <cfreturn THIS> </cffunction> <cffunction access="public" returntype="void" name="update"> <cfargument required="true" type="numeric" name="temperature"> <cfargument required="true" type="numeric" name="humidity"> <cfargument required="true" type="numeric" name="pressure"> <cfset VARIABLES.temperature = ARGUMENTS.temperature> <cfset VARIABLES.humidity = ARGUMENTS.humidity> <cfset display()> </cffunction> <cffunction access="public" returntype="void" name="display"> <cfoutput> Current conditions: #VARIABLES.temperature#F degrees and #VARIABLES.humidity#% humidity <br /> </cfoutput> </cffunction> </cfcomponent>
HeatIndexDisplay.cfc
<cfcomponent implements="Observer, DisplayElement" output="true"> <cffunction access="public" returntype="HeatIndexDisplay" name="init"> <cfargument required="true" type="WeatherData" name="weatherData"> <cfset VARIABLES.weatherData = ARGUMENTS.weatherData> <cfset VARIABLES.weatherData.registerObserver(THIS)> <cfreturn THIS> </cffunction> <cffunction access="public" returntype="void" name="update"> <cfargument required="true" type="numeric" name="temperature"> <cfargument required="true" type="numeric" name="humidity"> <cfargument required="true" type="numeric" name="pressure"> <cfset VARIABLES.temperature = ARGUMENTS.temperature> <cfset VARIABLES.humidity = ARGUMENTS.humidity> <cfset VARIABLES.heatIndex = computeHeatIndex(VARIABLES.temperature, VARIABLES.humidity)> <cfset display()> </cffunction> <cffunction access="public" returntype="void" name="display"> <cfoutput> Heat index is #VARIABLES.heatIndex# <br /> </cfoutput> </cffunction> <cffunction access="private" returntype="numeric" name="computeHeatIndex"> <cfargument required="true" type="numeric" name="t"> <cfargument required="true" type="numeric" name="rh"> <cfset var LOCAL = {}> <cfset LOCAL.index = ((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) + (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) + 0.000000000843296 * (t * t * rh * rh * rh)) - (0.0000000000481975 * (t * t * t * rh * rh * rh)))> <cfreturn LOCAL.index> </cffunction> </cfcomponent>
Test Page
WeatherStation.cfm
<cfset weatherData = createObject("component", "WeatherData").init()> <cfset currentDisplay = createObject("component", "CurrentConditionsDisplay").init(weatherData)> <cfset heatDisplay = createObject("component", "HeatIndexDisplay").init(weatherData)> <cfset weatherData.setMeasurements(80, 65, 30.4)> <cfset weatherData.setMeasurements(82, 70, 29.2)> <cfset weatherData.setMeasurements(78, 90, 29.2)>
Source Code
- Head First Observable ColdFusion Builder Project - headfirstobserver.zip