Building a Simple Command Application


Now that you've had a chance to read the theory behind the Command pattern, let's take a look at a sample application that uses the pattern.

In the sample application in this chapter, you'll use four commands to scale and rotate a display object. Each of the commands is then associated with a draggable display object. By dragging the display objects and dropping them on a button, you'll effectively re-wire the button to apply the new commands associated with the draggable display objects, illustrating the extensibility and interoperability of commands.

This application uses an interface and classes from the AAS3WDP library. Specifically, the application uses the ICommand, IUndoableCommand, and IRedoableCommand interfaces as well as the BasicButton class, which are defined in the AAS3WDP library. You'll want to add the AAS3WDP library to the classpath for the project you configure for this example application.

Creating the Commands

First we'll create the command classes this application uses. The first of these commands is the RotateClockwiseCommand class. Define the com.peachpit.aas3wdp.commandexample.commands.RotateClockwiseCommand class as follows:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.ICommand;    import flash.display.DisplayObject;    public class RotateClockwiseCommand implements ICommand{       private var _receiver:DisplayObject;             public function RotateClockwiseCommand(receiver:DisplayObject) {          _receiver = receiver;       }          public function execute():void {          _receiver.rotation += 20;       }       } }


In this command, the execute() method increments the rotation property of the receiver object by 20, effectively rotating the object clockwise.

Next, we'll create a command to rotate the object counterclockwise. Define the com.peachpit.aas3wdp.commandexample.commands.RotateCounterclockwiseCommand class as follows:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.ICommand;    import flash.display.DisplayObject;    public class RotateCounterclockwiseCommand implements ICommand {       private var _receiver:DisplayObject;       public function RotateCounterclockwiseCommand(receiver:DisplayObject) {          _receiver = receiver;       }             public function execute():void {          _receiver.rotation -= 20;       }        } }


You'll notice that the RotateCounterclockwiseCommand class looks almost identical to the RotateClockwiseCommand class except that it decrements the receiver object's rotation property by 20 rather than incrementing it.

Now we'll create a command class for scaling the receiver object up. Define the com.peachpit.aas3wdp.commandexample.commands.ScaleUpCommand class as follows:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.ICommand;    import flash.display.DisplayObject;    public class ScaleUpCommand implements ICommand {       private var _receiver:DisplayObject;       public function ScaleUpCommand(receiver:DisplayObject) {          _receiver = receiver;       }            public function execute():void {          _receiver.scaleX += .1;          _receiver.scaleY += .1;       }    } }


In this class, the execute() method increments the scaleX and scaleY properties of the receiver object by .1, causing the object to scale up.

And now we'll define a command class that scales the object down. Define the com.peachpit.aas3wdp.commandexample.commands.ScaleDownCommand class as follows:

package com.peachpit.aas3wdp.commandexample.commands {    import com.peachpit.aas3wdp.commands.ICommand;    import flash.display.DisplayObject;    public class ScaleDownCommand implements ICommand {       private var _receiver:DisplayObject;          public function ScaleDownCommand(receiver:DisplayObject) {          _receiver = receiver;       }       public function execute():void {              _receiver.scaleX -= .1;          _receiver.scaleY -= .1;       }    } }


This command works just like the ScaleUpCommand except that it decrements the scaleX and scaleY properties.

Creating a Receiver Type

The commands we defined in the preceding section require a receiver object. We'll now create a class whose instances we can use as receiver objects for the commands. The receiver objects must be of type DisplayObject, so our receiver type subclasses flash.display.Shape. We'll define com.peachpit.aas3wdp.commandexample.shapes.Rectangle, so that it draws a rectangle you can use as the receiver for the commands.

package com.peachpit.aas3wdp.commandexample.shapes {    import flash.display.Shape;    public class Rectangle extends Shape {       public function Rectangle(color:uint, side:Number) {          graphics.lineStyle();          graphics.beginFill(color, 1);          graphics.drawRect(-side / 2, -side / 2, side, side);          graphics.endFill();       }    } }


Creating a Button

Our application requires a button that we can wire up with a command. In order to accomplish this we'll use a subclass of BasicButton from the AAS3WDP library.

Here we define a new subclass of BasicButton called com.peachpit.aas3wdp.commandexample.controls.CommandButton. This class accepts a command and calls the execute() method when clicked.

package com.peachpit.aas3wdp.commandexample.controls {    import com.peachpit.aas3wdp.controls.BasicButton;    import com.peachpit.aas3wdp.commands.ICommand;    import flash.events.MouseEvent;    public class CommandButton extends BasicButton {       private var _command:ICommand;       public function set command(value:ICommand):void {          _command = value;       }       public function CommandButton(label:String) {          super(label);          addEventListener(MouseEvent.CLICK, onClick);       }       private function onClick(event:MouseEvent):void {          if(_command != null) {             _command.execute();          }       }    } }


The CommandButton constructor accepts a label parameter just like BasicButton, and it passes that along to the super constructor. It also automatically listens for mouse clicks. When the user clicks the button, it attempts to call the execute() method of a command object that was passed to it with a setter method. This means that we can assign different command objects to the button, and because the button is programmed to an interface (ICommand) rather than a specific implementation, the interface can run the commands successfully even if they have very different implementations.

Creating the Command Containers

For this application, we're going to associate instances of each command type with a draggable display object, which we'll call a command container. For this purpose, we'll define a class called com.peachpit.aas3wdp.commandcontainers.CommandContainer. This class is a drag-and-drop Sprite subclass that has a command object and applies it to a CommandButton instance if it is dropped over the button.

package com.peachpit.aas3wdp.commandexample.commandcontainers {    import flash.display.Sprite;    import flash.events.MouseEvent;    import flash.text.TextField;    import flash.display.DisplayObject;    import com.peachpit.aas3wdp.commands.ICommand;    import com.peachpit.aas3wdp.commandexample.controls.CommandButton;    public class CommandContainer extends flash.display.Sprite {       private var _command:ICommand;       private var _x:Number;       private var _y:Number;             public function CommandContainer(command:ICommand, labelText:String,          xValue:Number, yValue:Number) {          // Store a reference to the command object.          _command = command;          // Draw a rectangle.          graphics.lineStyle();          graphics.beginFill(0xFFFFFF, 1);          graphics.drawRect(0, 0, 50, 50);          graphics.endFill();          // Create a text field to use as the label.          var label:TextField = new TextField();          label.width = 50;          label.height = 50;          label.multiline = true;          label.wordWrap = true;          label.text = labelText;          label.selectable = false;          addChild(label);          // Listen for mouse events to enable the           // drag-and-drop behavior.          addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);          addEventListener(MouseEvent.MOUSE_UP, onMouseUp);          _x = xValue;          _y = yValue;          x = _x;          y = _y;      }      private function onMouseDown(event:MouseEvent):void {         startDrag();      }      private function onMouseUp(event:MouseEvent):void {         stopDrag();         x = _x;         y = _y;         // Get the current drop target using the inherited         // dropTarget property. (See ActionScript 3.0          // documentation for details on the property.)         var target:DisplayObject = dropTarget;         // The drop target can sometimes be an object within         // the object for which you want to test. For         // the target in this case could be a label inside         // a command button rather than the command button         // itself. Use a while statement to get the parent         // and assign it to the target variable in those         // cases.         while(target != null && !(target is CommandButton) && target != root) {            target = target.parent;         }         // If the target is a command button then set the         // command of the button to the command object         // associated with this container.         if(target is CommandButton) {            CommandButton(target).command = _command;         }                }    } }


The container constructor requires that you associate the container with a command object. The container is draggable. When the user drops the container on a command button, it then sets the command property of the button to the associated command object.

Testing the Application

Finally, we'll define the main class such that it adds an instance of Rectangle and CommandButton as well as four instances of CommandContainer, each with one of the command objects.

package {    import flash.display.Sprite;    import com.peachpit.aas3wdp.commandexample.commandcontainers.CommandContainer;    import com.peachpit.aas3wdp.commandexample.shapes.Rectangle;    import com.peachpit.aas3wdp.commandexample.commands.RotateClockwiseCommand;    import com.peachpit.aas3wdp.commandexample.commands.RotateCounterclockwiseCommand;    import com.peachpit.aas3wdp.commandexample.commands.ScaleUpCommand;    import com.peachpit.aas3wdp.commandexample.commands.ScaleDownCommand;    import com.peachpit.aas3wdp.commandexample.controls.CommandButton;    public class CommandExample extends Sprite {       public function CommandExample() {          var rectangle:Rectangle = new Rectangle(0xFFFFFF, 50);          rectangle.x = 200;          rectangle.y = 200;          addChild(rectangle);          var button:CommandButton = new CommandButton("apply command");          addChild(button);          button.y = 250;          var container:CommandContainer = new CommandContainer(new             RotateClockwiseCommand(rectangle), "rotate clockwise", 0, 0);          addChild(container);          container = new CommandContainer(new RotateCounterclockwiseCommand             (rectangle), "rotate counter-clockwise", 0, 55);          addChild(container);          container = new CommandContainer(new ScaleUpCommand(rectangle), "scale up",             0, 110);          addChild(container);          container = new CommandContainer(new ScaleDownCommand(rectangle),              "scale down", 0, 165);          addChild(container);       }    } }


When you test the sample application, you can drag and drop one of the command containers on the command button instance. That action wires the command button with the corresponding command object. Clicking the button then runs the command. For example, if you drag and drop the rotate clockwise container over the command button and then click the button, the rectangle will rotate clockwise.




Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net