Pages

Google+

Wednesday, February 10, 2010

Strategies For Global Data ( hey it happens... )

[ disclaimer: I see this technique as 'damage control' and not a best (or even good) practice. Yes, globals are to be avoided ]

Twitter is a wallflower's dream... anyone can eavesdrop and quietly judge from afar....
Last night Jesse Freeman , Ralph Hauwert and Robert Penner had a little discussion via Twitter about statics & globals. It starts here. Robert encapsulates the danger of globals with this:
Statics are evil when they puncture encapsulation and hide dependencies that impale anyone who tries to use them somewhere new.


Like Robert, I have a deep suspicion ( bordering on contempt ) for global data; however, it's really about how and why global data is used and not so much the concept itself. Steve McConnell in Code Complete nails it here:
If you use global variables indiscriminately or you feel that not being able to use them is restrictive, you probably haven't caught on to the full value of information hiding and modularity yet.

ouch...

But hey globals aren't ALL bad - globals don't make bad programs... programmers make bad programs...

In my ( admittedly small ) programming experience, globals almost universally appear in applications when the designer has painted them self into a corner, is lazy or just doesn't know any better. Architecturally a program shouldn't need globals and should instead use various other techniques; however, we need to be pragmatic and not throw out the baby with the bath water. First the bad...

Common problems with global data are:

  1. Inadvertent changes to global data ( or state )

  2. Spaghetti code and mind numbing conditionals that designers use to wrestle back control of an application

  3. Difficulty for code reuse

  4. Confusing initialization order of components (objects) that make code hard to read and debug


But, globals can be useful. AS3 does not natively support enums* and we can emulate it with a global 'typing' system, they're handy for some utility methods (think AS3 Math class) and other named constants.
Hey, doesn't Google own dependency injection framework guice use Singletons?

[edit: As Robert points out, I misinterpreted the functionality of guice's Singletons ]

Sometimes the circumstance may arise where there is a need for globaly accessible data. Never mind how you get there, but when you are in that spot, what's a girl to do?

My current technique of choice is what is suggested in Steve McConnell's in Code Complete; use access routines. Again, Steve sums it up best with:
Anything you can do with global data, you can do better with access routines.


 

Time For Some Code


Recently, I had to come back to a content management system I built a few years ago. In it, I was using a global Model to store my application's data.
Here is the class and data of concern.



package cms
{
public class Data_Model
{
public var images : Array;
private static var _instance:Data_Model ;

public static function getInstance():Data_Model
{
if( _instance==null ) { _instance = new Data_Model();}
return _instance ;
}

public function Data_Model()
{
if( _instance!=null ) throw new Error("Error: Use getInstance() method");
Data_Model._instance = this;
}
}
}

Pretty standard Singleton data model. The public variable "images" is an array of value objects. The app was small so I just combined my views and controllers into 2 hybrid view/controller classes. Now, in two different controller/views I use this data to create two different XML files. It goes something like this:
// view 1
private function export_xml():void
{
var xml_for_export = new XML();
for(var i:int; i< Data_Model.images.length; i++)
{
// create XML from value objects
}
}

// view 2
private function export_xml():void
{
var xml_for_export = new XML();
for(var i:int; i< Data_Model.images.length; i++)
{
// create XML from value objects
// differently than view 1
}
}

I didn't want or need to do any serious refactoring for this app, but I did think about two problems:

  1. Code should live with data - here they are separated

  2. How do I or someone else know where my data is accessed in my program


 

Enter Access Routines


To fix this I changed the array to a private, static member (to make sure I only have one of it and know that only 'Data_Model' can get to it) and then I added in my routine.
package cms
{
public class Data_Model
{
public static const XML_FORMAT_ONE = "XML_FORMAT_ONE";
public static const XML_FORMAT_TWO = "XML_FORMAT_TWO";

private static var images : Array;

public function get_xml_formatted_for_images(format:String):XML
{
var xml:XML = new XML();
if(format == this.XML_FORMAT_ONE)
{
// use images array to build xml
}
else if(format == this.XML_FORMAT_TWO)
{
// use images array to build different xml than above
}

return xml;
}
// singleton enforcer code taken out
}
}

// view 1
private function export_xml():void
{
var xml:XML = Data_Model.get_xml_formatted_for_images(Data_Model.XML_FORMAT_ONE);
}

Now I've solved my two chief concerns. I am manipulating data within the same scope as it is declared (code lives with data), and now it's easier to understand how my application may work and how data is accessed. Lastly, if I need to abstract or 'correct' my application, it's much, much easier to do because I have consolidated things.

So, this was just a quickie. I find it a good tool to have in the code toolbox.

*For more reading about enums in AS3 check out Alan Shaw's post .