Strategies For Global Data ( hey it happens… )
by Alan
[ 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:
- Inadvertent changes to global data ( or state )
- Spaghetti code and mind numbing conditionals that designers use to wrestle back control of an application
- Difficulty for code reuse
- 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:
- Code should live with data – here they are separated
- 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 .
[...] This post was Twitted by FlashArtOfWar [...]
You’ve made a doubleton. What happens when you want another model? You would add another constant and make a tripleton. This doesn’t scale very well.
The problem lies in retrieving a model dependency with a static call:
var xml:XML = Data_Model.get_xml_formatted_for_images(Data_Model.XML_FORMAT_ONE);
This breaks the locality of the context and decreases flexibility. If a component needs data, pass it in. Don’t have the component reach out to get it from a static API.
Imagine if a Flex List was hard-coded to retrieve its dataProvider from a static function. How would that be to work with?
You have a valid point about enums. Anything constant is fine as a static.
Guice has singletons per injector, not per virtual machine. Same with injectors used by Robotlegs. You can create as many injectors as you like, each with its own set of singletons.
http://groups.google.com/group/google-guice/browse_thread/thread/bf493035d428025b
Also, the singletons are not globally accessible. Some people say they are “lower-case” singletons rather than Singletons, which are global.
@robert
Sorry, I don’t follow the bit about “…add another constant and make a tripleton”. The constants I have in the example are just a way to set a naming convention for passing arguments to the accessor method. I think I’m missing something…
“This doesn’t scale very well”
You’re right it doesn’t! I think I will go back and reword a bit of the article
My point was really more of ‘you’re a coder on an app that *already* uses global data or ‘you’ve come into a project and there needs to be a quick fix.’
I see this technique as more of a ‘damage control’ rather than a best practice. It’s NOT meant to be scaled up at all. If anything it’s a band-aid before a heavy refactor.
Ok, cool.
I’m kinda confused here:
So lets say you have a singleton called GLOBAL(.as)
Is it better to have a routine (getter/setter) to access the variables than have them staticly typed?
E.g. GLOBAL.myVar vs GLOBAL.getVar()
I think a good point is “statics are evil… when they puncture encapsulation”
The purpose of having the routine (method/function) is to allow arguments to be passed in. This allows me to help contain and add some control to when/who modifies the data.
Having the data statically typed will help ensure that there is only one instance of it, but that won’t help the epic fail that globals can portend.
globals are evil for sloppy programmers. Basic assumption of new (28 yrs into programming) scripting languages are that programmers are a bunch of stupid idiots. That assumption may only hold true for the developers of these languages avoiding globals, table calls gotos and expression evaluators. It’s explanation is complete laziness during compiler end runtime environment development thats all. Programming like that becomes sometimes like boxing with your one hand tied to your one foot. We are leaving more than %70 of cpu into spider webs, we are not harnessing the power of the computers just because of this lazy people. I just wrote a vm inside as3 vm just because of this. That solved a lot of problems. If it was not that popular, I would say as3 is just a piece of crap and leave it alone for rot.Remember : OOP and Structural programming are just programming approaches and there are other approaches which may fit to applications. As3 is a very and severely crippled oop.For instance as with the first assumption I indicated in the beginning it does not have support for destructors or onDestroy event propagation. I rewrote a system just to avoid this successfully. But why do I have to do all this? To make as3 work properly. Kick your gc adobe… And a lot of other weirdos in your sloppy code.