squarewolf

Did you know…

Nov 7, 2010

Last week I was making an implementation of the command pattern, and while doing so I encountered some strange behavior in my flex code that I couldn’t explain… After about a day of puzzling I finally figured it out, it’s all in the arguments!

First I’ll get you up to date with my implementation of the command pattern. I basically used an interface ICommand and a CommandManager class.

package commands
{
    public interface ICommand
    {
        function get arguments():Object;
        function execute():void;
        function undo():void;
    }
}
package commands
{
    public class CommandManager
    {
        private static var _executed : Array = [];
        private static var _undone : Array = [];

        public static function execute(type:CommandType, args:Object):void
        {
            _undone = [];
            _executed.push( command );
            command.execute();
        }

        public static function undo():Boolean
        {
            if (_executed.length == 0)
                return false;

            var command : ICommand = ICommand(_executed.pop());
            command.undo();
            _undone.push(command);
            return true;
        }

        public static function redo():Boolean
        {
            if (_undone.length == 0)
                return false;

            var command : ICommand = ICommand(_undone.pop());
            command.execute();
            _executed.push(command);
            return true;
        }
    }
}

So far so good, no big issues here, right? Wrong. It’s already going down the drain here, and I’ll show you in a simple example using the SampleCommand class, which extends the AbstractCommand.

package commands
{
    public class AbstractCommand
    {
        private var _arguments:Object;

        public function AbstractCommand(arguments:Object)
        {
            _arguments = arguments;
        }

        public function get arguments():Object
        {
            return _arguments;
        }

        public function execute():void
        {
        }

        function undo():void
        {
        }
    }
}
package commands
{
    public class SampleCommand extends AbstractCommand
    {
        public function SampleCommand(name:String)
        {
            super(name);
        }

        public function execute():void
        {
            trace("Hello, " + arguments);
        }

        function undo():void
        {
            trace("Goodbye, " + arguments);
        }
    }
}

Both classes seem to be fine and compile without problem. However there is a hideous hidden feature messing the entire application up. As soon as you run this code you’ll get strange errors like “TypeError: Error #1034: Type Coercion failed: cannot convert []@86a1431 to …”.

Now that’s one strange Error! Or is it? The problem is that even though this code compiles, it’s not really doing what you’d say it would do. Because the arguments property we call in the execute and undo methods isn’t the arguments property from the ICommand interface! Things become much more clear when you have a look at the Global.as file in the Flex SDK. Do you see the problem arising at line 81? There is a class called arguments and because of the way Flex works it can be used perfectly as we use it in our code, it’s just not the arguments property we think we’re using!

The solution is as easy as can be: don’t use the word ‘arguments’ anywhere. Make it args or something, anything else will do, and your code will run fine.

Well, I hope you’ll remember this one! I sure know I will after an afternoon of debugging code that throws errors for no reason whatsoever!

 
Post comment as twitter logo facebook logo
Sort: Newest | Oldest
Design: YellowFrog