Tags: | Categories: Programming Posted by mharman on 6/2/2009 12:38 PM | Comments (0)

So for the last couple weeks, I have been learning about design patterns. I am admittedly not an expert or even sure I know what I'm doing so I'm hoping that if anything is incorrect, or could be improved, that a comment will be left.

The first pattern that I have read about is the Strategy pattern. Below is the UML diagram for a sample application I'm writing.

The point to the strategy pattern is to create a class with the attributes and methods that do not change together, but make provisions to easily add methods that implement a common interface but can yield different behavior. The Vehicle class is an abstract that is never instantiated. Instead, the classes GoCart and ModelCar will be subclasses of Vehicle and can contain methods specific for them. A go-cart can drive and turn but most plastic model cars usually don't. Each class can have a Year, Make and Model but there is some difference in how the implementation for drive and turn needs to happen. As you will see we can even change these behaviors at run-time.

Enough jabbering, here's some code. I'm using Flex and AS3 but it should be fairly easy to code it in any language./

This is my abstract class from which the concrete classes will extend.

Vehicle.as
package Strategy
{
  public class Vehicle
  {
    import Strategy.DriveBehavior;

    public function Vehicle()
    {
    }

    private var _year:int = 0;
    private var _make:String = '';
    private var _model:String = '';
    private var _driveBehavior:DriveBehavior;
    private var _turnBehavior:TurnBehavior;

    /* Accessors */
    public function setYear(year:int):void {
      _year = year;
    }

    public function getYear():int {
      return _year;
    }

    /* Other Getters and Setters Here */

    public function setDriveBehavior(behavior:DriveBehavior):void {
      _driveBehavior = behavior;
    }

    public function setTurnBehavior(behavior:TurnBehavior):void {
      _turnBehavior = behavior;
    }

    /* Functions */
    public function performDrive(){
      return _driveBehavior.Drive();
    }

    public function performTurn(){
      return _turnBehavior.Turn();
    }
  }
}

These are the concrete classes, an extension of the Vehicle class. It is in these classes that I set the default behavior for Turn and Drive.

GoCart.as
package Strategy
{
  public class GoCart extends Vehicle
  {
    public function GoCart()
    {
      super.setDriveBehavior(new RearDrive());
      super.setTurnBehavior(new TurnFront());
    }
  }
}

 

ModelCar.as
package Strategy<
{
  public class ModelCar extends Vehicle
  {  
    public function ModelCar()
    {
      super.setDriveBehavior(new NoDrive());
      super.setTurnBehavior(new NoTurn());
    }
  }
}

Here are my two interfaces for the behaviors used to define the required functions that any of the behavior classes must implement.

DriveBehavior.as
package Strategy
{
  public interface DriveBehavior
  {
    function Drive():String;
  }
}

 

TurnBehavior.as
package Strategy
{
   public interface TurnBehavior
   {
     function Turn():String;
   }
}

For the behaviors, I'll post one for each of the interfaces. They simply return a string indicating that the behavior is active.

RearDrive.as
package Strategy
{
  public class RearDrive implements DriveBehavior 
  {
    public function RearDrive()
    {
    }


    public function Drive():String 
    {
      return "Driving using the back wheels.";
    }
  }
}

 

TurnFront.as
package Strategy
{
  public class TurnFront implements TurnBehavior
  {
    public function TurnFront()
    {
    }

    public function Turn():String 
    {
      return "Turning from the front."
    }
  }
}


Now for the code that pulls it all together.

DesignPatters.mxml
  
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
    <![CDATA[
      import Strategy.GoCart;
      import Strategy.RearDrive;
      import Strategy.FrontDrive;
      import Strategy.NoTurn;
      import Strategy.TurnFront;

      private var myGoCart:GoCart = new GoCart();

      private function init():void 
      {
        AutoYear.text = myGoCart.getYear().toString();
        AutoMake.text = myGoCart.getMake();
        AutoModel.text = myGoCart.getModel(); 
        AutoDrive.text = "---";
        AutoTurn.text = "---"; 
        myGoCart.setDriveBehavior(new RearDrive());
        myGoCart.setTurnBehavior(new TurnFront());  
      }

      private function setValues():void 
      {
        myGoCart.setYear(2002);
        myGoCart.setMake("Home-Built");
        myGoCart.setModel("Racer");
        AutoYear.text = myGoCart.getYear().toString();
        AutoMake.text = myGoCart.getMake();
        AutoModel.text = myGoCart.getModel();
        AutoDrive.text = myGoCart.performDrive();
        AutoTurn.text = myGoCart.performTurn();
      }
   
      private function changeBehavior():void 
      {
        myGoCart.setDriveBehavior(new FrontDrive());
        myGoCart.setTurnBehavior(new NoTurn());
      }

    ]]>
  
  <mx:Panel width="200" height="300">
    <mx:Label text="Go Cart"/>
    <mx:Label id="AutoYear"/>
    <mx:Label id="AutoMake"/>
    <mx:Label id="AutoModel"/>
    <mx:Label id="AutoDrive"/>
    <mx:Label id="AutoTurn"/>
    <mx:Button label="Show Values" click="setValues()" />
  </mx:Panel>
</Application>
    

This just imports the appropriate classes, initializes an instance of the GoCart class and shows a default display in a panel. Click the Show Values button and the labels are populated with the Year, Make, Model and the two default behaviors. Clicking the Change Behavior button calls a function that modifies that default behavior. Again click the Show Values button to see the changes.

Not too exciting but it was an interesting exercise. Again, I'm doing this as a learning exercise so I appreciate any comments you may have and if I have it completely wrong, please feel free to set me straight. :)

If you are interested, you can download the files.