2 Replies Latest reply on May 20, 2007 9:58 AM by snafu7x7

    singletons and static initializers oh my

    snafu7x7 Level 1
      I'm trying to write a Settings class that will load in configuration items from a custom xml file. In typical OO this is often handled by implementing a Singleton design pattern (to ensure only a single instance is created ever) and using static initializers to load static properties when the class is loaded. Since AS3 doesnt allow private constructors using a static initializer seemed to make even more sense. My code below illustrates what I'm trying to accomplish. Basically when the class is first loaded I want it to call my LoadConfig() to populate my settings into an XMLListCollection. I then have a GetConfigItem(key:String) method that will filter thru the XMLListCollection seeking the key and return its associated value.

      The problem is the order of events doesn't occur as expected.

      If in my application I call

      var test:Settings = Settings.instance; //try to force the class to load
      Settings.GetConfigItem("somekey");

      you would expect to not even have to use the first line because the minute you invoke the second it 'should' load the class but I put it in to try to force it to load. So one would expect it to load and initialize the properties and by the time you made the next call to LoadConfig(), the Config variable would be initialized. What happens though is that the LoadConfig isn't getting called until AFTER I make my call to GetConfigItem so the Config object is null and I get an exception. To me this seems to completely violate the way these constructs are supposed to work.

      The only thing I can think might be happening is that the HttpService call is running asynchronously and so the results are not available when expected??? Please help gang, this one's got me stumped...if I've made some boneheaded mistake forgive me, its Friday ;)



      public final class Settings{
      private static var _instance:Settings = new Settings();
      {
      LoadConfig();
      }
      public static var Config:XMLListCollection

      public function Settings()
      {
      if (_instance != null)
      {
      throw new Error("Singleton can only be accessed through Singleton.instance");
      }

      }

      public static function get instance():Settings
      {
      return _instance;
      }

      public static function LoadConfig():void{
      var url:String = "config.xml";
      var service:HTTPService = new HTTPService();
      service.resultFormat = "e4x";
      service.url = url;
      service.addEventListener(ResultEvent.RESULT, onConfigLoad);
      service.send();
      }

      private static function onConfigLoad(event:ResultEvent):void{
      Config = new XMLListCollection(event.result.*);
      }

      public static function GetConfigItem(itemKey:String):String{
      var filteredList:XMLListCollection = Config.(@key.indexOf(itemKey) > -1);
      //return the value associated with the key
      }


      }
        • 1. singletons and static initializers oh my
          VarioPegged Level 2
          Your guess is correct: Actionscript executes asynchronously, so you'll have to handle everything you're trying to do via events and listeners.


          Try this (added code in bold):


          import flash.events.EventDispatcher;
          import flash.events.Event;

          public final class Settings{
          private static var _instance:Settings = new Settings();
          public static var dispatcher:EventDispatcher = new EventDispatcher();
          {
          loadConfig();
          }
          public static var config:XMLListCollection

          public function Settings()
          {
          if (_instance != null)
          {
          throw new Error("Singleton can only be accessed through Singleton.instance");
          }
          }

          public static function get instance():Settings
          {
          return _instance;
          }

          public static function loadConfig():void{
          var url:String = "config.xml";
          var service:HTTPService = new HTTPService();
          service.resultFormat = "e4x";
          service.url = url;
          service.addEventListener(ResultEvent.RESULT, onConfigLoad);
          service.send();
          }

          private static function onConfigLoad(event:ResultEvent):void{
          config = new XMLListCollection(event.result.*);
          dispatcher.dispatchEvent(new Event(Event.COMPLETE));
          }

          public static function getConfigItem(itemKey:String):String{
          var filteredList:XMLListCollection = config.(@key.indexOf(itemKey) > -1);
          //return the value associated with the key
          }
          }

          Then from within your app you could call your getConfigItem() either directly or via the event listener based on whether the data is available yet (i'm assuming you're making only one service call):

          public var myKey:String = "somekey";
          [Bindable]
          public var myData:String;

          private function callConfig():void
          {
          if (Settings.config == null) { //no data available yet, so add the event listener
          Settings.dispatcher.addEventListener(Event.COMPLETE, getConfig);
          } else {
          myData = Settings.getConfigItem(myKey));
          }
          }

          private function getConfig(e:Event):void
          {
          myData = Settings.getConfigItem(myKey));
          Settings.dispatcher.removeEventListener(Event.COMPLETE, getConfig);
          }

          • 2. Re: singletons and static initializers oh my
            snafu7x7 Level 1
            thanx alot Vario, yes that does make sense...I don't come from a Flash background so some of the finer points of AS are lost on me, especially when they differ from Java, C#, etc.

            It looks like your approach will work fine but I'm curious if perhaps there is a way to do it cleaner without littering my calls to Settings.getConfigItem() with event handling code to check if the value is initialized (alot of my settings are used very early on in the application). For my purposes it would be ideal to have some kind of a loader up front that would handle the initialization of my config and when complete kick off the rest of the application. That way I'm always certain that my settings have been initialized. Do you know of a good way to do this? I know my Flash developer buddies would often talk of writing 'loaders' to do similar sort of things so I assume theres an easy way to do this in Flex.

            thanx again