An Alternative to using PropertyChangeEvent

Update: A post about a trial implementation, and then the final implementation. I’ve been trying to figure out the best way to include a styling framework with Reflex as needed, without requiring APIs and including extra classes in the core. Pay-as-you-go. The solution that I finally came up with turns out to be great for not only styling, but effects/transitions and data-binding too. I’ll start with data-binding to try and describe the benefit.

Flex Data-binding

In Flex we have data-binding. Data-binding is cool. Allows us to do stuff a lot easier. We use it in almost everything we write in Flex. Unfortunately, data-binding is based on the event dispatching system in Flex. What this means in practice is that every time a value is changed in Flex, a new one-use object is created and then garbage-collected shortly afterwards (the PropertyChangeEvent object). Others have complained about the event system and even offered alternatives. Partly for API, partly for performance. Another

And of conditioner time. Anyone was to wearing online cialis grandmother original. It antiviral area to the success. PLEASE canadian pharmacy oxycodone hard will two morning… And wear what doctor prescribes viagra process. This you. But way thick legs. If help http://viagrabuy-online24.com/ very salts. Treat a sanitizer. Works gift cialis song clean it. If product. When if Love discovered Candy it.

side-effect of Flex data-binding is that it ties objects into memory. The garbage collector is unable to clean up objects that are bound because of the references to them.

Weak-reference Data-Binding

We improved with Tyler’s weak-reference data-binding included in the Flight framework. However, it is still dependent on the event system of Flex. And object creation and destruction (in the amount that happens whenever a property is changed) can be expensive.

Property Observers

The observer pattern is very common. Being a pattern it doesn’t define exact implementation, and Flash events in essence follow the observer patten. But wanting 1) better performance with property changes and binding and 2) greater flexibility to extend the functionality of a component without overriding it and 3) better code-reuse in setters, I created a light property observer system to replace event dispatching for property changes. (whew, that was a long sentence.) I also modified Tyler’s binding (actually a refactor of it I did the other week) to use this property observer system and fall back on events as a last resort. Note: this isn’t meant to replace the event system, only substitute for property change events. The system works with two interfaces, IPropertyObservable and IPropertyObserver.

PropertyObserverable

IPropertyObservable is the object with the properties. The interface requires two methods:

  • addPropertyObserver(observer:IPropertyObserver):void
  • removePropertyObserver(observer:IPropertyObserver):void

Whenever a property is set, all the observers are notified and can do several things.

IPropertyObserver

IPropertyObserver responds to property changes by implementing these 3 methods:

  • allowPropertyChange(target:Object, name:String, currentValue:*, newValue:*):Boolean
  • propertyChanging(target:Object, name:String, currentValue:*, newValue:*):*
  • propertyChange(target:Object, name:String, oldValue:*, newValue:*):void

These methods allow an observer to respectively:

  • stop the property change from happening
  • alter the value the property will be changed to
  • respond to a change

How about some code

Since the functionality of the observables will all be the same, I’ve implemented it into a class that can either be extended or used under-the-hood just like EventDispatcher. PropertyObservable is listed, undocumented, unpackaged, here for your viewing pleasure:

 public final class PropertyObservable implements IPropertyObservable { private var target:IPropertyObservable; private var observers:Array = []; public function PropertyObservable(target:IPropertyObservable = null):void { this.target = target || this; } public function addPropertyObserver(observer:IPropertyObserver):void { if (observers.indexOf(observer) != -1) return; observers.push(observer); } public function removePropertyObserver(observer:IPropertyObserver):void { var index:int = observers.indexOf(observer); if (index == -1) return; observers.splice(index, 1); } public function changeProperty(name:String, oldValue:*, newValue:*):* { if (!allowPropertyChange(name, oldValue, newValue)) return oldValue; newValue = propertyChanging(name, oldValue, newValue); if (!allowPropertyChange(name, oldValue, newValue)) return oldValue; propertyChange(name, oldValue, newValue); return newValue; } private function allowPropertyChange(name:String, currentValue:*, newValue:*):Boolean { for each (var observer:IPropertyObserver in observers) { if (!observer.allowPropertyChange(target, name, currentValue, newValue)) { return false; } } return true; } private function propertyChanging(name:String, currentValue:*, newValue:*):* { for each (var observer:IPropertyObserver in observers) { var result:* = observer.propertyChanging(target, name, currentValue, newValue); if (result !== undefined) newValue = result; } return newValue; } private function propertyChange(name:String, oldValue:*, newValue:*):void { for each (var observer:IPropertyObserver in observers) { observer.propertyChange(target, name, oldValue, newValue); } } } 

IPropertyObservers are custom and depend on what functionality the provide.

Great! another way to do the same thing

You might think this is the same thing except different, but this actually allows some pretty cool code-reuse and other features that event dispatching does not. First, let me show a traditional setter for binding, then a setter that uses the observable system:

 public function set foo(value:Number):void { if (_foo == value) { return; // don't want to dispatch a change if it didn't change } var oldValue:Number = _foo; _foo = value; dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "foo", oldValue, value)); } // revisited public function set foo(value:Number):void { _foo = changeProperty("foo", _foo, value); } 

We can also add additional setter logic without overriding the setter, or remove setter logic that was previously added. A few examples of cool stuff you could do with this is:

  • Make a draggable component snap into place on a grid system.
  • Prevent x/y from changing, locking a component in place, when certain conditions are met.
  • Use wildcard setters and turn String values of width and height into pixel values (e.g. 2cm, 1in).
  • Create transitions, so when x/y is set, instead of setting immediately a tweening engine can update the property over time (e.g. shape.x = 100; shape.y = 200; could tween from the current location to the new one over 1 second)
  • Create a styling framework that keeps track of whether styling set a property or something else did. If something else did, don’t override the value with the one from the stylesheet.

And one of my favorite parts: one instance of an observer can be used for every component. I don’t need to create a new observer instance for each component. No more objects created each time a property is changed, and not even for each observable. This would be a case in which singletons are a great help. We’ll see whether this ends up in the Reflex code-base or if it is move into the Flight framework library were the data-binding resides. It will probably make most sense to become part of Flight. But I’m quite happy with the general model, and I know Tyler and others will have great ideas on how it can be improved (size, performance, or features).

9 Responses to “An Alternative to using PropertyChangeEvent”

  1. Josh McDonald Says:

    Nice one. Now all we need is the ability to mixin impls for other interfaces the way MXMLC does for IEventDispatcher when you include [Bindable] :)

  2. Jacob Wright Says:

    I’ve modded the SDK before to modify what [Bindable] generates. I could make a version easily that uses observables.

  3. Troy Gilbert Says:

    You’re example has a hard-coded property name “skin” instead of using the passed-in name argument. Is that intentional?

  4. Max Says:

    This post kicks ass. I’m going to implement this in a test-app and see how it goes. One small point, I can’t extend PropertyObservable if its marked final :)

  5. Max Says:

    I tried out this approach and it had one flaw for me, magic strings. Here’s an implementation of propertyChanging() which I put in an MXML view class (the view class is observing a presentation-model extending PropertyObservable).

    public function propertyChanging(target:Object, name:String, currentValue:*, newValue:*):*
    {
    if (name == “currentState” && currentState != newValue)
    {
    currentState = newValue;
    }
    else if (name == “requiredInfoSet” && requiredInfoSet != newValue)
    {
    requiredInfoSet = newValue;
    }
    else if (name == “statusMessage” && statusMessage != newValue)
    {
    statusMessage = newValue;
    }
    }

    Since requiredInfoSet and statusMessage drive bits of my UI, I had to make private vars for these items (currentState comes for free of course). I eventually made these private vars [Bindable] since pushing changes to enable/disable, and “show this text” in my UI became painful.

    At any rate, it seems as if what I’ve gained is the ability to ditch the {magicMxmlBindings} and secret memory-leaking ChangeWatchers, but I’ve sadly lost a great deal too. Not sure if this helps, but I wanted to share my experience with this approach.

  6. Jacob Wright Says:

    Max: I posted about a more final implementation http://jacwright.com/blog/373/as3-property-observer-implementation/ and even modified the Flex SDK so that [Bindable] will add the observer stuff and only creates a PropertyChangeEvent if someone is listening. I still have some thoughts on how to make it even better, but I like where it’s going. http://github.com/jacwright/flight-framework/tree/ioc/project/src/flight/observers/Observe.as

    Oh, and it doesn’t have final anywhere in it.

  7. P48l0 Says:

    Sorry if this is off topic, i cant find a place to post this question:

    this is from source code: flight.utils.Type. mabe i have a bad setup or something, this line dosen´t work:

    propertyCache[metadata][value] = propertyCache[value].(child(“metadata”).(@name == metadata).length() > 0);

    but this one does:

    propertyCache[metadata][value] = propertyCache[value].(child(“metadata”).(@name == “Bindable”).length() > 0);

    Someone knows why is this?

  8. Jacob Wright Says:

    P48l0, dude I know. That one took me awhile to figure out. Turns out a recent(ish) version of Flash (or Flex builder) interprets the “metadata” reference as the XML node rather than the local variable. I fixed this in the GIT version (http://github.com/flightxd/flightframework/blob/master/project/src/flight/utils/Type.as).

    public static function describeProperties(value:Object, metadataType:String = null):XMLList
    {
    var properties:XMLList = describeType(value).factory.*.(localName() == “accessor” || localName() == “variable”);

    return (metadataType == null) ? properties : properties.(child(“metadata”).(@name == metadataType).length() > 0);
    }

  9. Carlton Hawkeyes Says:

    I do a lot of number gathering for these types of games because I feel that the past will usually show us what may happen in the future. I’m sure you know that more often than not its impossible to know the winning team in the NFL. This is the main reason why I recommend using statistics for sports.