Design Patterns

Strategy Pattern

Defition

Define a family of algorithms, encapsulate each one, and make them interchangeable.

定義一組演算法,把演算法封裝,並且它們之間可以互換。

Class Diagram

strategy pattern

Role

  • Strategy

    指定了如何使用不同的演算法

  • ConcreteStrategy

    實作了這些不同的演算法

  • Context

    使用演算法的類別

Implements

IStrategy.as

package
{
    public interface IStrategy
    {
        function exe():String;
    }
}

ConcreteStrategyA.as

package
{
    public class ConcreteStrategyA implements IStrategy
    {
        public function exe():String
        {
            // do Something
            // ...
            // ...

            return "Concrete Strategy A exe!!!!";
        }
    }
}

ConcreteStrategyB.as

package
{
    public class ConcreteStrategyB implements IStrategy
    {
        public function exe():String
        {
            // do Something
            // ...
            // ...

            return "Concrete Strategy B exe~~~";
        }
    }
}

Context.as

package
{
    public class Context
    {
        private var _strategy:IStrategy;

        public function Context(strategy:IStrategy){
            _strategy = strategy;
        }

        public function exeStrategy():String{
            return _strategy.exe();
        }
    }
}

Main.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication 
  xmlns:fx="http://ns.adobe.com/mxml/2009" 
  xmlns:s="library://ns.adobe.com/flex/spark" 
  xmlns:mx="library://ns.adobe.com/flex/mx">
  <fx:Script>
    <![CDATA[
      protected function but_clickHandler(event:MouseEvent):void
      {
        var thinker:Context;
        if(event.target.id == "butA"){
          thinker = new Context(new ConcreteStrategyA);
        }
        if(event.target.id == "butB"){
          thinker = new Context(new ConcreteStrategyB);
        }
        txtArea.text = thinker.exeStrategy();
      }
    ]]>
  </fx:Script>
  <s:VGroup width="100%" height="100%" verticalAlign="middle" horizontalAlign="center">
    <s:HGroup>
      <s:Button id="butA" label="Strategy A" click="but_clickHandler(event)"/>
      <s:Button id="butB" label="Strategy B" click="but_clickHandler(event)"/>
    </s:HGroup>
    <s:TextArea id="txtArea"/>
  </s:VGroup>
</s:WindowedApplication>

在這邊, 我們看到 thinker = new Context(new ConcreteStrategyA) 這段程式碼決定了要用什麼策略. 決定了要用什麼策略, 然後再去執行它 thinker.exeStrategy()

另一種形式

another style

首先, 創建一個 interface, 它要執行 sort 策略 ISortStrategy.as

package
{
    // Strategy
    public interface ISortStrategy
    {
        function exe(a:Array):Array;
    }
}

我們分別會有三個 class 來實作這個 ISortStrategy SortSimple, SortAll, SortDesc

SortSimple.as

package
{
    public class SortSimple implements ISortStrategy
    {
        public function exe(a:Array):Array{
            a.sort();
            return a;
        }
    }
}

SortAll.as

package
{
    public class SortAll implements ISortStrategy
    {
        public function exe(a:Array):Array{
            a.sort(Array.CASEINSENSITIVE);
            return a;
        }
    }
}

SortDesc.as

package
{
    public class SortDesc implements ISortStrategy
    {
        public function exe(a:Array):Array{
            a.sort(Array.CASEINSENSITIVE | Array.DESCENDING);
            return a;
        }
    }
}

Context.as

package
{
    public class Context
    {
        protected var sortStragegy:ISortStrategy;

        // exeStrategy
        public function doSort(a:Array):Array{
            return sortStragegy.exe(a);
        }
    }
}

ConcreteContext.as

package
{
    public class ConcreteContext extends Context
    {
        public function ConcreteContext()
        {
            sortStragegy = new SortDesc();
        }
    }
}

Main.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication 
  xmlns:fx="http://ns.adobe.com/mxml/2009" 
  xmlns:s="library://ns.adobe.com/flex/spark" 
  xmlns:mx="library://ns.adobe.com/flex/mx"
  creationComplete="initComp(event)">
  <fx:Script>
    <![CDATA[
      import mx.events.FlexEvent;
      protected function initComp(event:FlexEvent):void{
        var thinker:Context = new ConcreteContext;
        var friends:Array = new Array("John","abc","Amy","Ben","Zana","Helen");
        txtArea.appendText(thinker.doSort(friends).toString());
      }
    ]]>
  </fx:Script>
  <s:TextArea id="txtArea" fontSize="30" width="500"/>
</s:WindowedApplication>

例子

Role(角色) PassiveSkill UniqueSkill
Teemo(提摩) Camouflage(偽裝) Noxious Trap(毒性陷阱)
Lee Sin(李星) Flurry(無雙亂舞) Dragon's Rage(神龍擺尾)

定義被動技的 interface.

IPassiveSkill.as

package
{
    // 被動技
    public interface IPassiveSkill
    {
        function exe():String;
    }
}

定義大招的 interface.

IUniqueSkill.as

package
{
    // 大招
    public interface IUniqueSkill
    {
        function exe():String;
    }
}

天生技能類別(偽裝)

Camouflage.as

package
{
    public class Camouflage implements IPassiveSkill
    {
        // Teemo's passive skill
        public function exe():String
        {
            return "Camouflage";
        }
    }
}

天生技能類別(Flurry)

package
{
    public class Flurry implements IPassiveSkill
    {
        // LeeSin's passvie skill
        public function exe():String
        {
            return "Flurry";
        }
    }
}

大招技能類別

NoxiousTrap.as

package
{
    public class NoxiousTrap implements IUniqueSkill
    {
        // Teemo's unique skill
        public function exe():String
        {
            return "CrescentSweep";
        }
    }
}

大招技能類別

DragonRage.as

package
{
    public class DragonRage implements IUniqueSkill
    {
        // Lee Sin's unique skill
        public function exe():String
        {
            return "DragonRage";
        }
    }
}

角色類別

Role.as

package
{
    public class Role
    {
        protected var passiveSkill:IPassiveSkill;
        protected var uniqueSkill:IUniqueSkill;

        public function exePassiveSkill():String{
            return passiveSkill.exe();
        }

        public function exeUniqueSkill():String{
            return uniqueSkill.exe();
        }

        /*
        public function setPassiveSkill(addPassiveSkill:IPassiveSkill):void{
            passiveSkill = addPassiveSkill;
        }

        public function setUniqueSkill(addUniqueSkill:IUniqueSkill):void{
            uniqueSkill = addUniqueSkill;
        }
        */
    }
}

提摩類別

Teemo.as

package
{
    public class Teemo extends Role
    {
        public function Teemo()
        {
            passiveSkill = new Camouflage;
            uniqueSkill = new NoxiousTrap;
        }
    }
}

李星類別

LeeSin.as

package
{
    public class LeeSin extends Role
    {
        public function LeeSin()
        {
            passiveSkill = new Flurry;
            uniqueSkill = new DragonRage;
        }
    }
}

程式入口點

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    creationComplete="initComp(event)">
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            protected function initComp(event:FlexEvent):void
            {
                var teemo:Role = new Teemo;
                txtArea.text = "Teemo\n"
                txtArea.text += teemo.exePassiveSkill() + "\n";
                txtArea.text += teemo.exeUniqueSkill() + "\n";
                // teemo.setPassiveSkill(new Flurry) + "\n";
                // txtArea.text += teemo.exePassiveSkill() + "\n\n";

                trace("\n\n")

                var leeSin:Role = new LeeSin;
                txtArea.text += "LeeSin\n"
                txtArea.text += leeSin.exePassiveSkill() + "\n";
                txtArea.text += leeSin.exeUniqueSkill() + "\n"
            }

        ]]>
    </fx:Script>
    <s:TextArea id="txtArea" text=""/>
</s:WindowedApplication>