Today I’ll talk about a concept — a design pattern used throughout the Flex framework — called property invalidation.
Let’s start by looking at a problem:
<mx:Clock hours="18" minutes="70" />
The above is an MXML declaration of a hypothetical Clock component. The component has the usual time property that represents the absolute time, but it also has convenience properties for hours, minutes and seconds.
In the above example, the time is being set to 18 hours and 70 minutes. As you would expect, the component tries to automatically adjust the values to 19 hours and 10 minutes — except for one potential bug.
The problem is that there’s no guarantee of the order in which the properties are set on the component. If hours is set first, all is fine, but if minutes is set first, the time ends up as 18 hours and 10 minutes.
Here’s the minutes setter (using ActionScript 3):
public function set minutes(value:int):Void
{
if (value >= 60)
{
_hours += Math.floor(value / 60);
_minutes = value % 60;
}
else
_minutes = value;
}
First the hours is 0, and the minutes setter sets the time to 1:10; then when hours is set, it overwrites the old value, making it 18:10!
Several examples of such interdependcies between component properties exist. Consider the video component with its maintainAspectRatio set to true: depending on which of width and height is set first, the other one has to be adjusted accordingly.
The solution is to postpone any side effects resulting from the setting of properties until a later point in the component’s lifecycle. In Flex, the “later point” is a screen update — just before the component draws itself.
Here’s the new minutes setter:
public function set minutes(value:int):Void
{
_minutes = value;
invalidateProperties();
}
override public function commitProperties():Void
{
if (_minutes >= 60)
{
_hours += Math.floor(_minutes / 60);
_minutes %= 60;
}
}
The call to the invalidateProperties method tells the framework that some of the component’s properties have been “touched”, which results in the component’s commitProperties method being called on the next screen update. In commitProperties, we do any final adjustments to the time values before anything gets drawn on the screen.
Often you want to postpone a costly operation until it’s confirmed that it’s really required. See the following piece of ActionScript code, for example:
box.setStyle("borderStyle", "solid");
...
if (useNewTheme)
box.setStyle("borderStyle", "none");
Here, the border style is being set to “solid” first, but further down in the same block of code, depending on a condition, it’s being set back to “none”. If the component were to create a border (a new sprite) immediately upon setting of the style to “solid”, it would be a waste. The right thing to do here (which the Flex Framework components do) is to postpone the decision until the next screen update — using invalidateProperties and commitProperties.