Avoid Global Data

Word Count: 566

I have just started to read a new book titled The pragmatic programmer. I am quickly realizing why everyone has dubbed this book a must have for all programmers. I am only into the 2nd chapter but I already found something I would like to share with you. This may seem like a no brainer to most of you but to some beginners this is a very important lesson to building a reusable & scalable system.

So how and why would we avoid global data. Lets first examine what The Pragmatic programmer would tell us about global data.

Every time your code references global data, it ties itself into the other component that share that data. In general your code is easier to understand and maintain if you explicitly pass any required context into your modules. In object oriented applications, context is often passed as parameters to objects constructors.
Basically what this tells us is that it is poor design to reference global data in our component when we can create a more maintainable & reusable component by passing that data as a parameter. We will look at some code below to see how this applies in our world.

Lets say for example that in our Application component we define some global data and place it into our application scope like so.

<cfset application.dsn = "myDSN">
<cfset application.dbuser = "user">
<cfset application.dbpass = "pass">

This is code that we all have written at one time (or something similar) and there is nothing wrong with this code. Here is where our mistake is made. Lets say we create an Employee component and in the component we need to access our database. We need to know these parameters so beginners may have the desire to write the following code.

<cfcomponent>
   <cffunction name="getAll" access="public" returntype="query">
      <cfset var local = "">
      <cfquery name="local" datasource="#application.dsn#">
      SELECT * FROM Employee
      </cfquery>
      <cfreturn local>
   </cffunction>
</cfcomponent>

So your thinking to yourself what is wrong with that. It is maintainable because if our dsn changes for some reason we only have to change it in one place. You would be right but it also is not very reusable. The best answer is to stay away from global data and pass it into your component as an instance parameter. So how do we do that, simple lets look at the answer.

<cfcomponent>
   
   <cfset variables.dsn = "">
   
   <cffunction name="init" access="public" returntype="Employee">
      <cfargument name="dsn" type="string" required="true">
      <cfset variables.dsn = arguments.dsn>
      <cfreturn this>
   </cffunction>
   <cffunction name="getAll" access="public" returntype="query">
      <cfset var local = "">
      <cfquery name="local" datasource="#variables.dsn#">
      SELECT * FROM Employee
      </cfquery>
      <cfreturn local>
   </cffunction>
   
</cfcomponent>

This code makes our component maintainable and reusable. Now we could pass our global data into the component. The moral of the story kids, avoid global data in your components.

<cfset empObj = createObject("component","employee").init(application.dsn)>
<cfset employees = empObj.getAll()>


Comments

#1 Posted By: Peter Bell Posted On: 2/20/07 9:06 AM
Or at least minimize it as much as possible. The dirty little secret of MG, M2 and every home built framework I've ever seen (including all of mine) is that there are a bunch of things we need to pass around the application, so we just dump them into some kind of bucket and then make calls against that bucket all over the application - tying at the very least our views and controllers to the frameworks implementation of "bucket".

Unfortunately, the only other alternative is to have a *lot* of arguments for every single call and as you add more stuff to be passed around it becomes a nightmare to keep all of the interfaces up to date.

Moral of the comment: do try to avoid application scoped data, but don't think throwing it into a bucket and passing it round solves the problem - it just moves it over.

Wherever possible, try to use the "bucket" as little as possible and understand the trade off you are making by using what is effectively an undocumented (in terms of method signatures) interface. Also, at all costs decouple your model from the bucket - never pass the MG "event" or the M2 "event-Args" object to your model.
#2 Posted By: Sammy Larbi Posted On: 2/20/07 10:44 AM
I agree with the sentiment here, but I also recognize what Peter alludes to - you need to balance the complexity issue.

The value of "moving around" application scoped data and the like is, if you need to use that component for a one-off call, you might not want to set application scoped data, for instance.

In cfrails, I've used the session to pass a message around in one case, but I don't like it. The trouble is, I haven't discovered a way to *not* pass the message that way and still acheive my usability goals. Although, I will consider other solutions as I think of them.
#3 Posted By: Jay Posted On: 6/26/07 3:01 PM
So, how would you avoid using application.dsn in a component you're using with flash remoting via flash forms?

In a nutshell you have the connection object pointing at the gateway+component...then your AS funcs make remoting calls like blah.myGlobalObjects.remoteServicesGateway.mycomponent.mycomponentmethod(args);

...so I'm not sure (maybe I"m a noob?) about how to call that init first as part of the remoting setup?

Sure I could pass the name of the datasource as an arg. to the remote function...but then that data source name is hard-coded in the flash form - ?
#4 Posted By: Dan Vega Posted On: 6/26/07 3:06 PM |
Author Comment
Jay,
I have to be honest, I have not done much work with flash remoting so I can not shed much light on the subject. Is there a way to pass in a dsn as an argument? The point with global data is that you really do not want to rely on it.


Post Your Comment

Leave this field empty







Show Captcha

If you subscribe, any new posts to this thread will be sent to your email address.

Copyright © 2007 Dan Vega | BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.