Our first entry comes from Steve Gustafson. Before reading on, please check his application here. Now that you have played with it a bit, let's take a look at selected portions of the code. Let's start with his Application.cfc file:

<cfcomponent output="false"> <cfsetting showdebugoutput="NO"> <cfscript> this.name="gusBlackJack"; this.sessionManagement = true; this.sessionTimeOut = CreateTimeSpan(0,0,20,0); this.scriptProtect = true; </cfscript>

<cffunction name="onApplicationStart"> </cffunction>

<cffunction name="OnSessionStart"> <cfset session.playedCardList = ''> </cffunction>

<cffunction name="OnRequestStart"> <cfargument name = "request" required="true"/>

</cffunction> </cfcomponent>

I want to point out a few lines in particular. First off - notice how he turns off debugging. Why would you do this? ColdFusion debugging can be turned on at the server level, and if you forget to restrict debugging to a particular IP, then everyone in the world will see your debugging information. That's definitely not something you want. I'm a big fan of "Being Anal" in regards to stuff like this. You will notice I do the same in BlogCFC (although I use an Application.cfm file there instead). So - a minor point - but something to keep in mind.

In keeping with the "Being Anal" thread, he also specifies a session timeout and a scriptProtect. Specifying a sessiontimeout is a good idea since in CFMX 7, there was a bug where onSessionEnd wouldn't fire without it. (This has been fixed in 7.01.) ScriptProtect is a handy way to easily protect against cross-site scripting. It isn't perfect - but it is still a good idea to use if you aren't sure you are protecting against it in code yourself.

Now let's take a quick look at index.cfm. I'm only going to point out one thing here and it is more of a minor nit-pick than anything else. The following is from the top of the file, and is not the entire file itself:

<cfsetting showdebugoutput="No"> <!--- This section handles the ajax requests ---> <cfif isDefined('url.action')> <cfset bjOBJ = createObject("component","blackjack")/> <cfif url.action EQ 'hitMe'> <cfset thisRslt = bjObj.dealCard()> <cfoutput>#thisRslt#</cfoutput><cfabort> <cfelseif url.action EQ 'newGame'> <cfset thisRslt = bjObj.newGame()> <cfoutput>#thisRslt#</cfoutput><cfabort> </cfif> </cfif> <!--- end ajax section ---> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Gus's CF BlackJack</title> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="xmlhttp.js" type="text/javascript"></script> <script language = "javascript"> var playerBank = 100; var wager = 0; var playerVal = 0; var dealerVal = 0; var hasAce = 0; </script> </head>

As you can see, he put the code to handle AJAX requests at the top of the file. To me, this doesn't belong here. I'd have a separate file just to handle those requests. Again, this is just my opinion and nothing more. I will, however, harp on something more. Notice how he creates the component for every request. This confused me a bit until I looked at his CFC:

<cfcomponent displayname="BlackJackByGus" hint="This component handles the backend for the Blackjack Application>">

<cffunction name="shuffle" hint="I select a random value" returntype="numeric" access="private" output="No"> <cfargument name="maxNum" type="numeric" required="True"> <cfreturn RandRange(1, arguments.maxNum)> </cffunction>

<cffunction name="getSuit" hint="I select the suit to be dealt" returntype="string" output="No" access="private"> <cfset suit = shuffle(4)> <cfif suit EQ 1> <cfset strSuit = 'Spades'> <cfelseif suit EQ 2> <cfset strSuit = 'Hearts'>
<cfelseif suit EQ 3> <cfset strSuit = 'Diamonds'>
<cfelse> <cfset strSuit = 'Clubs'>
</cfif> <cfreturn strSuit> </cffunction>

<cffunction name="getCard" hint="I select the card to be dealt" returntype='numeric' output="No" access="private"> <cfset card = shuffle(13)> <cfreturn card> </cffunction>

<cffunction name="dealCard" hint="I select the card to be dealt" returntype="string" output="No" access="public"> <cfset cardNum = getCard()> <cfset cardSuit = getSuit()> <cfset thisCard = "#cardSuit#_#cardNum#"> <cfif ListFind(session.playedCardList, thisCard)> <cfreturn dealCard()>
<cfelse> <cfset session.playedCardList = listAppend(session.playedCardList,thisCard)> <cfif cardNum LT 10> <cfset cardValue = "#thisCard#|#cardNum#|#session.playedCardList#"> <cfelse> <cfset cardValue = "#thisCard#|10|#session.playedCardList#"> </cfif>

<cfreturn cardValue> </cfif> </cffunction>

<cffunction name="newGame" hint="I begin a new game of blackjack" returntype="boolean" output="No" access="public"> <cfset session.playedCardList = ''> </cffunction> </cfcomponent>

What worried me was - how is he keeping track of what cards have been dealt. If you look at CFC, you will see that he references a session variable, playedCardList, to store the used cards. Typically, it is a bad idea to reference any outside scope in a CFC. How would I have done this differently?

First, I would have stored the playedCardList as a variable in the CFC itself. Secondly, I would have then stored the CFC in the session scope. This would kill two birds with one stone. First off - it makes my CFC a bit better in that it is no longer tied to the session scope. If I decide to switch to some other scope, it would be easier since I'm not having to change the CFC itself. Secondly, I would then need to check for the existence of the CFC before creating an instance. This will make the application run a bit quicker since we would only run createObject() once per session.

Another nit - nowhere in his CFC does he use the var scope, and every method (but shuffle) is missing var statements. In getSuit, for example, this line:

<cfset suit = shuffle(4)>

Should be:

<cfset var suit = shuffle(4)>

Similar changes are needed in his other methods. This is a serious problem. Not using the var scope can lead to some very hard to debug problems. It's times like this where I wish I could tell CF to be 'strict' and not let me create variables like that. (I know, I know, CF wasn't built for it. ;)

So, that's it. All in all, this is a nice submission. Good job, Steve!

Download attached file.