"Static" properties and methods belong to a class, rather than to an instance of a class, meaning that they are shared, in a sense, across your entire project. But when should you use the static keyword?
Consideration 1:
When There Can Be Only One
This is the basic requirement. If a property needs to have independent values across multiple instances, then the property can’t be static.
However, if you have, say, a TableCell class that gets instantiated once for every cell in a table layout, you might end up with some numeric layout values that are the same for every TableCell, no matter what the cell’s contents or position. A padding
or margin
property, or a text color, font name, or background color, is likely to not change from instance to instance.
In this case, you might consider making a static property.
Consideration 2:
When You Need “Global” Access
A static property or method acts somewhat like a globally accessible function or variable. Simply import the class, and you can read the same value from anywhere else in the program.
It’s usually not advisable to rely on global variables as they can lead to spaghetti code, but that’s not to say that they can’t be used. Sometimes the simplest solution is the best one. This works well with constants (see “con2-global-access” in the source download).
For some methods, however, you might consider making a package function, which is more global in the traditional sense than a static method (see “con2-package-functions” for an example). That only works with functions, though, so if you need global variables, or need to reference other class members, you will have to use static properties and methods.
Consideration 3:
When You Need Utility Functions
Sometimes you just need a function. For example, consider the following:
function removeAllChildren(doc:DisplayObjectContainer):void { var num:Number = doc.numChildren; for (var i:uint = 0; i < num; i++) { doc.removeChildAt(0); } }
This is a useful function, and it would ideally live in a class for easy reuse. By making a class, perhaps called DisplayObjectUtils
, and making this function a static method on that class, you have a convenient place to store this function. This makes even more sense as you gather a few related functions into one class. And because the methods are static, there is no need to create a DisplayObjectUtils
instance any time you want to use the utility function.
Flash ships with a set of easing classes in the package fl.motion.easing
. Each of these classes has a mere three static methods, following the naming convention easeIn
, easeOut
, and easeInOut
. The classes are basically storage containers for functions used elsewhere. There is no point in creating a Quadratic
object, as it exists solely for its static methods.
See the example project “con3-utility-functions” for a simple but effective DisplayObjectUtils
class in action.
Consideration 4:
When You Need Enumerated Values
Occasionally it’s necessary to use a value defined by another object within your code. Suppose you have a single object that can be set up one of two ways; maybe it’s a button that can have either a red or black background. And you set that text up in the constructor.
Now, if you had properties that acted as flags for either the red or black text, then you’d have a chicken-and-egg scenario where you couldn’t get to those properties before the constructor was run, but you couldn’t run the constructor without those properties.
Fortunately, static properties save us from this conundrum. This becomes a possibility:
new StyledButton(StyledButton.RED);
As ActionScript lacks any kind of enumerated type, static constants provide the next best thing.
An existing example of this would be event names that are stored as static constants. For example, Event.COMPLETE
or MouseEvent.CLICK
. Imagine if you had to create a new MouseEvent
every time you want to set up a click listener. Note that this example also falls under the “only one” umbrella; we certainly wouldn’t want the click event name to be defined in multiple places.
Also, see the project “con4-enum-values” for a custom example.
Consideration 5:
When You Need a Singleton (Or Singleton-Like Behavior)
This is kind of a combination of “only one” and “global access,” as that’s sort of the definition of a Singleton. However, I’d like to point out that the proper Singleton design pattern isn’t necessary to achieve similar effects, and that static methods are the key to this.
By way of example, the ExternalInterface
class consists of two static methods and three static properties (addCallback()
and call()
are the methods, and the properties are available
, marshallExceptions
, and objectID
). Since there is only ever one external interface for the Flash Player, there isn’t much need to create multiple instances. Static members here make a lot of sense.
Static properties and methods are, of course, integral to the Singleton pattern, which I won’t be discussing here. If you want to learn more, there was a previous Quick Tip on the subject. But you may want to consider a singleton-like class without the need to create an instance. Sometimes it makes sense.
This is purely my opinion, but given the lack of private constructors (a key ingredient in the Singleton) in AS3, making a class that never needs instantiated seems to be a suitable alternative.
Consideration 6:
When You’re Not As Concerned With Performance
Now, before the performance optimization zealots aficionados flame me in the comments, please note that there are performance considerations with static members. In my testing, static members are a hair slower than instance members (we’re talking a few milliseconds over one million iterations). However, others have found — using tests far more exhaustive my own — that static members can have a substantial impact on performance. Check out Jackson Dunstan’s test results or Hristo Dachev’s exploration of the subject.
Regardless of how fast static access is, you should also consider that static members are initialized up front when the class is loaded into the runtime, whether or not you create an instance right away. My understanding of this is that it takes about the same amount of time to initialize a static property as it does an instance property with the same value, the difference is when that time is taken.
Put another way, let’s say it takes the Flash Player 10 milliseconds to declare a variable named foo
as a String
, and also set it with a String
value of “bar”. It will take 10 milliseconds to perform that action regardless of whether the variable is a static or an instance variable. So, in the following hypothetical QuickTip
class, we’d have a total of 20 milliseconds devoted to creating our properties:
package { public class QuickTip { public static var fooStatic:String = "bar"; public var fooInstance:String; public function QuickTip() { fooInstance = "bar"; } } }
However, those two properties take their 10 milliseconds at different times. So, considering this other bit of code (let’s assume it’s a document class for our SWF):
package { public class QuickTipTest extends Sprite { public function QuickTipTest { stage.addEventListener(MouseEvent.CLICK, onClick); } private function onClick(e:MouseEvent):void { var qt:QuickTip = new QuickTip(); } } }
Because the QuickTip
class is required and included, fooStatic
takes its 10 milliseconds while everything else is being initialized (class declarations, objects that are created right away, other static members). But fooInstance
never gets initialized until the stage is clicked, at which point a QuickTip
instance is created.
It’s probably a trivial distinction in most cases, but I am obliged to mention it. Keep in mind that my point here with this tip is more about architectural considerations, and less about performance tuning.
Comments