Command Pattern [Part 1]

| Comments

Well this is i think the most common pattern that i use in my apps it’s not the exact implementation but it works for me =D

here’s and example on how i use it

Problem: You have a Menu Instance and each Menu Item needs to execute different actions

Solution the fastest way

package
{

    import flash.display.MovieClip;
    import flash.events.MouseEvent;
        import flash.net.navigateToURL;
    import flash.net.URLRequest;
        import flash.external.ExternalInterface;
    import com.grupow.controls.WButton;
    /**
    * @author Raúl Uranga
    */

    public class Main extends MovieClip
    {
        private var container:MovieClip;
        private var _items:Array;

        public function Main() {

            _items = new Array();

            container = new MovieClip();

            var labels:Array = ["go to grupow.com", "go to google.com", "openPopUp"];

            for (var i:int = 0; i < 3; i++) {
                var item:WButton= new WButton();
                item.label = labels[i];
                item.y = i * 20;
                                item.addEventListener(MouseEvent.CLICK, click_handler, false, 0, true);
                _items.push(item);
                container.addChild(item);
            }
            addChild(container);

        }

        private function click_handler(e:MouseEvent):void
        {
            switch (e.target)
            {
                case _items[0] :
                    navigateToURL(new URLRequest("http://www.grupow.com"), "_blank");
                    break
                case _items[1] :
                    navigateToURL(new URLRequest("http://www.google.com"), "_blank");
                    break
                case _items[2] :
                    if (ExternalInterface.available) {
                        ExternalInterface.call("popUp", "http://www.vincehuston.org/dp/behavioral_rules.html", "Rules of thumb", 500, 700, 1,1);
                    }
                    break
            }
        }

    }
}

this is the easiest and fastest way, but that doesn’t mean it is the correct way… what if i need to add another behavior?? what if instead of having 3 menu items the menu grows to 20 or 30 items? the switch statement will go crazy and becomes too hard to understand and read!

Solution: The Command Pattern =D

lets get our hands dirty

first we create the interface

package com.grupow.commands
{
    /**
    * @author Raúl Uranga
    */
    public interface ICommand
    {
        function execute():void;
    }
}

then we separate the execution code and convert it in to Classes that implement the ICommand Interface

PopupCommand

package com.grupow.commands
{
    import flash.external.ExternalInterface;
    /**
    * @author Raúl Uranga
    */
    public class PopupCommand implements ICommand
    {
        private var url:String;
        private var title:String;
        private var width:Number;
        private var height:Number;
        private var resizable:Number;
        private var scrollbars:Number;

        public function PopupCommand(url:String, title:String, width:Number, height:Number, resizable:Number, scrollbars:Number)
        {
            this.url = url;
            this.title = title;
            this.width = width;
            this.height = height;
            this.resizable = resizable;
            this.scrollbars = scrollbars;
        }

        /* INTERFACE com.grupow.commands.ICommand */

        public function execute():void
        {
            if (ExternalInterface.available) {
                ExternalInterface.call("popUp", this.url, this.title, this.width, this.height, this.resizable, this.scrollbars);
            }
        }

    }

}

GetURLCommand

package com.grupow.commands
{
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    /**
    * @author Raúl Uranga
    */
    public class GetURLCommand implements ICommand
    {
        private var url:String;
        private var window:String;

        public function GetURLCommand(url:String = null, window:String = null)
        {
            this.url = url;
            this.window = window;
        }

        /* INTERFACE com.grupow.commands.ICommand */

        public function execute():void
        {
            navigateToURL(new URLRequest(url), window);
        }
    }
}

it’s a good practice to use NullObjects to avoid Null Instances

NullCommand

package com.grupow.commands
{
    /**
    * @author Raúl Uranga
    */
    public class NullCommand implements ICommand
    {

        public function NullCommand()
        {

        }

        /* INTERFACE com.grupow.commands.ICommand */

        public function execute():void
        {
            //null
        }
    }
}

ok Raúl, nice……… you just create a bunch of classes! so what??? well here’s the implementation… -our new Button:

package com.grupow.display {

    import flash.display.MovieClip;
    import com.grupow.controls.WButton
    import com.grupow.commands.ICommand;
    import com.grupow.commands.NullCommand;

    /**
    * ...
    * @author Raúl Uranga
    */

    public class CommandButton extends WButton
    {
        public var command:ICommand;

        public function CommandButton() {
            super();
            command = new NullCommand();
        }

        public function execute():void
        {
            command.execute();
        }
    }
}

notice that we initializated the command variable with a NullCommand Object so we don’t have to worry about Null References Erros

the Main App becomes to……..

package
{

    import flash.display.MovieClip;
    import flash.events.MouseEvent;

    import com.grupow.commands.*;
    import com.grupow.display.CommandButton;

    /**
    * @author Raúl Uranga
    */

    public class Main extends MovieClip
    {
        private var container:MovieClip;
        private var _items:Array;

        public function Main() {

            _items = new Array();

            container = new MovieClip();

            var labels:Array = ["go to grupow.com", "go to google.com", "openPopUp"];

            var commands:Array =  [new GetURLCommand("http://www.grupow.com", "_blank"),
                                   new GetURLCommand("http://www.google.com", "_blank"),
                                   new PopupCommand("http://www.vincehuston.org/dp/behavioral_rules.html", "Rules of thumb" , 500, 700, 1, 1)];

            for (var i:int = 0; i < 3; i++) {
                var item:CommandButton = new CommandButton();
                item.label = labels[i];
                item.command = commands[i] as ICommand;
                item.y = i * 20;
                _items.push(item);
                container.addChild(item);
            }

            container.addEventListener(MouseEvent.CLICK, click_handler, false, 0, true);
            addChild(container);

        }

        private function click_handler(e:MouseEvent):void
        {
            if (e.target is CommandButton) {
                e.target.execute();
            }
        }
    }
}

the cool thing is that i don´t have to worry about wich button was clicked! so we repleace something like this

                private function click_handler(e:MouseEvent):void
        {
            switch (e.target)
            {
                case _items[0] :
                    navigateToURL(new URLRequest("http://www.grupow.com"), "_blank");
                    break
                case _items[1] :
                    navigateToURL(new URLRequest("http://www.google.com"), "_blank");
                    break
                case _items[2] :
                    if (ExternalInterface.available) {
                        ExternalInterface.call("popUp", "http://www.vincehuston.org/dp/behavioral_rules.htmll", "Rules of thumb", 500, 700, 1,1);
                    }
                    break
            }
        }

in to this!!!!!!!

private function click_handler(e:MouseEvent):void
{
         if (e.target is CommandButton) {
                e.target.execute();
         }
}

and……..

-we separate the execution code -we remove duplicated code -we can reuse Objects and make them switchable -code is more human readable -if you need to add another behavior you just create another Command Class (this is part 2… coming soon)

obviously this way of doing it consumes more time but at the end it has more benefits.

source

cheers!

Comments