性能优化2: 使用单Listener函数和固定函数呼叫 Performance Tip 2: Mono Listener Function and Fixed Function Call

Gloss PNGDaemon_Tools Flex's event mechanism is powerful and prompts loose coupling. However, there are times you should not over use it for the sake of simplicity and performance.

Use fixed function call if components are tightly coupled

For example, we have a class called UIUnit and UIGroup. A UIUnit belongs to one UIGroup. One requirement for UIGroup is to display a 'save' button whenever there is an input change in any UIUnit.

Version 1 - Using the event mechanism:

class UIGroup {
  var units:ArrayCollection;
  
  public function UIGroup(units_:ArrayCollection) {
    this.units = units_;
    for(var i:int = 0; i < units.length; i++) {
      (units.getItemAt(i) as UIUnit).addEventListener("changed", onChanged);
    }
  }
  
  public function onChanged(event:Event):void {
    // display the 'save' button if it has not been shown yet.
  }
}

class UIUnit extends EventDispatcher {
  function onTextInput() { // called when there is input change on UI
    dispatch(new ChangeEvent());
  }
}

It works. However, it requires a lot of resources. For example, if there are 100 UIUnits in the group. Each UIUnit needs a hash map object to store all listeners and a ChangeEvent object will be generated for each text input change. Let's suppose each UIUnit generates 5 such events. So basically the overhead for this event dispatching are 100 hash map objects + 500 event objects. Now, let's see version 2.

class UIGroup {
  var units:ArrayCollection;
  
  public function UIGroup(units_:ArrayCollection) {
    this.units = units_;
    for(var i:int = 0; i < units.length; i++) {
      (units.getItemAt(i) as UIUnit).group = this;
    }
  }
  
  public function onChanged(event:Event):void {
    // display the 'save' button if it has not been shown yet.
  }
}

class UIUnit extends EventDispatcher {
  var group:UIGroup;
  
  function onTextInput() { 
    group.onChanged(null);
  }
}

Version 2 does not use event dispatching. As a result, the overhead is removed. The flexibility has been reduced though. You can not add extra listeners to a UIUnit. However, this tradeoff is definitely acceptable in this situation since UIGroup and UIUnit are tightly coupled.

Use mono listener function if there is at most one event listener

In the above example, the components are tightly coupled. Is it possible to have components loose coupled without Flex's formal event dispatching mechanism? Yes, when there is only one possible event listener. For example, if we want to monitor the UIGroup's change status to enable/dispable the 'save' menu item on menu bar. We do not need UIGroup to have formal dispatch functions. Instead, we simply use a mono listener function:

class UIGroup {
  var units:ArrayCollection;
  
  public var changeListenerFunc:Function; // the mono listener function
  
  public function UIGroup(units_:ArrayCollection) {
    this.units = units_;
    for(var i:int = 0; i < units.length; i++) {
      (units.getItemAt(i) as UIUnit).group = this;
    }
  }
  
  public function onChanged(event:Event):void {
    // display the 'save' button if it has not been shown yet.
    ...
    if(changeListenerFunc != null) { // notify the listener if any
      changeListenerFunc(event); 
    }
  }
}

class MyApplication {
  var group:UIGroup;
  
  function MyApplication() {
    group.changeListenerFunc = onGroupChange; // register the listener.
  }
  
  function onGroupChange(event:Event) { 
    // update menu item 'save'.
  }
}

Which technique to use? There are tradeoffs. You need to consider the contexts and select wisely.