views:

350

answers:

5

Let's say I have a Custom Event Class, and it has several Event types stored in static Constant:

package customEvents
{
    public class 
    {
        public static const DISAPPEAR_COMPLETELY:String = "disappearCompletely";

        public static const SIT_DOWN:String = "sitDown";

        public static const STAND_UP:String = "standUp";

        public static const SAIL_TO_THE_MOON:String = "sailToTheMoon";

        public static const GO_TO_SLEEP:String = "goToSleep";

        public static const GO_SLOWLY:String = "goSlowly";

        public function MyCustomEvent(type:String)
        {
            super(type);
        }
    }
}

Is there an easy way to validate that the type passed to the Constructor is one of the Static Constants of the Class, without having to check it against each value?

A: 

If you were to make each of the static constants as lower-case Strings of the upper-case constant names, then you could do:

if( class.hasOwnProperty( type.toUpperCase() ) )
{
    // Do something
}
else
{
    // Failure
}
Christopher W. Allen-Poole
A: 

There are a few ways you can attack this:

  1. Use an enum approach for the types to guarantee it's in the list. The parameter in the constructor would no longer be a string, but an enum. See http://blog.petermolgaard.com/2008/11/02/actionscript-3-enums/ for a way to replicate enums in AS3.
  2. Use a static constructor in your class to populate the list of types into an ArrayCollection and use the .contains() method to check for membership. (Possible implementation below)

Your code sample seems to be an event class. If that's the case, you can't use the first option, as you wouldn't want to change the signature of the constructor if you don't need to.

package customEvents
{
    public class 
    {
        private static var typeList:ArrayCollection;

        public static const DISAPPEAR_COMPLETELY:String = "disappearCompletely";
        public static const SIT_DOWN:String = "sitDown";
        <... lines deleted ...>
        public static const GO_SLOWLY:String = "goSlowly";

        // static constructor    
        {
            typeList = new ArrayCollection();

            typeList.addItem(DISAPPEAR_COMPLETELY);
            typeList.addItem(SIT_DOWN);
            <... lines deleted ...>
            typeList.addItem(GO_SLOWLY);

        public function MyCustomEvent(type:String)
        {
            if (typeList.contains(type)
                super(type);
            else
                throw new Error("Invalid type");
        }
    }
}
Dan R
+1  A: 

To extend off of Dan R's answer, you could create a strict event (like the way you'd do enums) class like so:

import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;

public class StrictEvent
{ 
    private static var VALID_EVENTS:Dictionary = new Dictionary();

    public static function initEvents(inType:*):void {
        var events:Object = {};
        var description:XML = describeType(inType);
        var constants:XMLList = description.constant;
        for each(var constant:XML in constants) {
          events[inType[constant.@name]] = true;
        }
        VALID_EVENTS[getQualifiedClassName(inType)] = events;
    }

 public function StrictEvent(type:String)
 {
  var className:String = getQualifiedClassName(this);
  if(VALID_EVENTS[className][type]) {
   // init
  } else {
   throw new Error("Error! " + type);
  }
 }


  }

Then, you could define your custom event class by extending the strict event class and calling initEvents in the static initializer. Here is your example with this method:

public class CustomEvent extends StrictEvent
{

    public static const DISAPPEAR_COMPLETELY:String = "disappearCompletely";

    public static const SIT_DOWN:String = "sitDown";

    public static const STAND_UP:String = "standUp";

    public static const SAIL_TO_THE_MOON:String = "sailToTheMoon";

    public static const GO_TO_SLEEP:String = "goToSleep";

    public static const GO_SLOWLY:String = "goSlowly";

    public function CustomEvent(type:String) {
  super(type);
 }    

    {
      initEvents(CustomEvent);
    }

}

Now, each time it creates an event it only has to look up the event object and see if the type is in that object. It also means you don't have to manually add all the constants in the initializer.

mweiss
+1  A: 

While both of these answers offer different solutions to validating the string values, they don't answer the question about having to check the values. Which you have to do, at the very least because the values of the constants are Strings themselves.

So the answer is no -- not if you're extending Event, anyway. Since that initial parameter has to be a String (insofar as you're extending Event), no matter how many levels of indirection you use, you'll still end up checking the arguments against a list of valid values. Passing in anything other than a String will throw a runtime error.

Christian Nunciato
I think he's asking the question of whether you have to check EACH one O(N), as opposed to checking a hash O(1).
mweiss
+1  A: 

Christian is correct -- and the question contains a somewhat ambiguous term. When you are checking for event "types", they are not datatypes, i.e. class objects, they are simply string values that pass along the particular "flavour" of the event to the listening object.

Personally, I don't think you need runtime validation of event types. If your code attempts to use an event constant which does not exist, such as

obj.addEventListener(CustomEvent.TYPE_WHICH_DOES_NOT_EXIST, handlerMethod);

...you're going to get a compiler error before your code is even run. If you use raw string values for event types, then you're defeating the purpose of using event constants anyways.

Joeflash
To play devil's advocate, what if CustomEvent.TYPE_WHICH_DOES_NOT_EXIST is instead replaced by a variable that's determined in a less simple way?
mweiss
@mweiss, as in a simple string value? The worst case is you will just never hear an event, so your handler will never execute.
Tyler Egeto
You could use a variable in place of the event type constant, but somewhere along the way you're going to have to pass a value to that variable prior to declaring the listener. If that value is a type constant which does not exist, you're still going to get a compilation error. If that value is a straight string, then you're not using events correctly. Event type constants are just variables to hold the string values of the various events, to allow for compile-time type checking.
Joeflash
... Because of this, the event type constant does not even need to be defined in the same class as the custom event, even though that is the best practise to facilitate encapsulation.
Joeflash
Agree with Joeflash. If you're passing a reference to "one of the Static Constants of the Class" to the Event constructor, the compiler will validate it for you. If you're not, it feels a bit like addressing potential programming mistakes not by correcting the code, but adding more code to detect the mistakes (which is a silly practice).
Niko Nyman