---
# System prepended metadata

title: Strategy (1)
tags: [Design Pattern, Creational Pattern]

---

---
tags: Design Pattern,Creational Pattern 
title: Strategy (1)
---
{%hackmd Sy5Quc5JY %}
# <font color="#F59F00">**Strategy (1)**</font>
>**Design Pattern rule #0 : In software, the requirments always change.**
>**軟體開發的世界裡，需求會一直在改變。**

>**Design Pattern rule #1 : Encapsulate what varies.**
>**把所有可能會更動的地方獨立出來 不要跟不會變動的地方混在一起。**

>**Design Pattern rule #2 : Program to an interface, not an implementation.**
>**我們應該依賴可變的虛擬，而不是固定的實作。**

>**Design Pattern rule #3 : More Has-a, less ls-a.**
>**多用合成，少用繼承。** 
## <font color="#F59F00">**Inheritnce**</font>
Inheritance 是一個很好 reuse code 的方式。
* **Example**
    紅頭鴨 `RedheadDuck` 、和綠頭鴨 `MallardDuck` ，我們不需要寫兩個想同的方法 `swim` ，我們只要寫一個 `Duck` 去繼承它就好。<br>
    <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/strategy1.png" width="400" height="320"></img></span>
    `Display` 是一個 abstract method，每個繼承 `Duck` 的 class 都會執行。
*    <font color="#92d1ff">**Problems & Resolution**</font>
    Q : 如果今天來了個黃色小鴨?
    A : 
    <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/strategy2.png" width="400" height="320"></img></span>
    橡皮鴨雖然不會叫，但只要 override `quack` 就好。
    Q : 根據 ==**rule #0**== ，今天突然要求要在 `Duck` 裡面加 `fly` 這個方法。
    A : 為了 reuse `fly` 這個方法，所有會飛的鴨子不用重寫，但是不會飛的鴨子就一一 override，
    Q : 但其實這又是一個問題，對於不會飛的鴨子來說，`fly` 這個方法也是 duplicate code，且如果最後不會飛的鴨子比較多，我們難道反而要把 `fly` 改成 `cant_fly`?
    Q : 更重要的問題，compile time 是寫死的，如果今天在程式運作時鴨子突然會飛了。要從 `cant_fly` 變成 `fly` 呢?
    A : 跟據 ==**rule #1**== ，那我們可以使用 Interface 把會改動的功能分開，會使用再把它實作就好啦，也就是下圖。
## <font color="#F59F00">**Interface**</font>
* **Example**
    <br>
    <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/strategy3.png" width="550" height="280"></img></span>
    虛線是實作，實線是繼承。
    DecoyDuck 不會飛也不會叫，所以都不用 implement ，要是要再新增一個 `walk` 直接實作它就好了。
*    <font color="#92d1ff">**Problems & Resolution**</font>
        Q : 你有發現一件事，這個 code 的 reuse 想當的爛，所有會 `fly` 的方法都要重寫一遍，如果今天 `fly` 要改，那全部的 `fly` 都要改，而且 compile time 寫死的問題沒有解決，那要怎麼改?
        A : 跟據 ==**rule #1、rule #2**==，我們把動作 `fly` `quack` 分開，用一個 Interface 並用各自的 SubClass 去實作，而不是在 Duck 的 SubClass。
        <br>
        <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/strategy4.png" width="500" height="200"></img></span>
        `QuackBehavior` `FlyBehavior` 兩個 interface 各自有各自的 subclass 去 implement。<br>
        <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/strategy5.png" width="750" height="400"></img></span><br>
        ```java
            interface QuackBehavior
            {
                public void quack();
            }
            interface FlyBehavior
            {
                public void fly();
            }
            class Quack extends QuackBehavior
            {
                //Quack
                //quack();
            }
            class Squack extends QuackBehavior
            {
                //Squack
                //quack();
            }
            class Mute extends QuackBehavior
            {
                //Mute
                //fly();
            }
            class FlyWithWings extends FlyBehavior
            {
                //FlyWithWings
                //fly();
            }
            class FlyNoWay extends FlyBehavior
            {
                //FlyNoWay
                //quack();
            }
            interface Duck
            {
                FlyBehavior flyBehavior;
                QuackBehavior quackBehavior;
                public void setFlyBehavior(FlyBehavior flyBehavior)
                {
                    //...
                }
                public void performFly()
                {
                    //...
                }
                public void setQuackBehavior(QuackBehavior quackBehavior)
                {
                    //...
                }
                public void performQuack()
                {
                    //...
                }
                public void swim()
                {
                    //...
                }
            }
            class MallardDuck extends Duck
            {
                public MallarDuck()
                {
                    quackBehavior = new Quack();
                    fiyBehavior = new flyWithWings();
                }
                public void display()
                {
                    System.out.println("isMallardDuck");
                }
            }
            //...
        ```
        在每個 `Duck` 的 subclass 只要事先在 construct 裡面先 new 好一個 instance 就好了，而且因為有 `setFlyBehavior` 我可以在 compile time 時決定它什麼時候會飛，如果有新的飛行方式，去重新 implement `flyBehavior` 就好，不需要改變其他 client 的用法。
        一隻鴨子的實作
        <br>
        ```java
            MallardDuck mallard = new MallarDuck();
            //FlyWithWings 的實作
            mallardDuck.performFly();
            //FlyWithRockets 的實作
            mallardDuck.setFlyBehavior(new FlyWithRockets());
            mallardDuck.performFly();
            //Quack 的實作
            mallardDuck.performQuack();
            //Squack 的實作
            mallardDuck.setQuackBehavior(new Squack());
            mallardDuck.performQuack();
        ```
## <font color="#F59F00">**Strategy Pattern**</font>
**定義演算法家族(interface)，並把每個演算法(concrete impl)封裝起來，演算法之間可以彼此互換，這個的模式，讓演算法的變動不影響演算法的使用方式，把抽象的方法用 interface 抽離，把實作留給 interface 的 subclass**
*    <font color="#92d1ff">**Strategy Pattern 的結構**</font><br>
        <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/strategy6.png" width="580" height="200"></img></span><br>
        *    <font color="#98c37a">**Context : Duck**</font>
                用 ConcreteStrategy 的物件，來設定狀態。
                <br>
                ```java
                flyBehavior = new FlyWithWings();
                ```
        *    <font color="#98c37a">**Strategy : FlyBehavior**</font>
                制定所有演算法的共同介面 Context 透過這個 interface 呼叫 ConcreteStrategy 實作的演算法。
                <br>
                ```java
                flyBehavior.fly();
                ```
        *    <font color="#98c37a">**ConcreteStratrgy : FlyWithWings**</font>
                跟據 Strategy 的 interface 實作演算法。
*    <font color="#92d1ff">**Conclusion**</font>
        *    **Example**
                根據rule #3，鴨子的行為不是繼承而來，而是由其他行為物件組成。
        *    <font color="#98c37a">**Pros**</font>
                1. Strategy 可以針對同一種行為有不同的實作。
                2. Strategy interface 還可以 hierarchy ， 繼承共同的功能。
                3. 對於 Context 來說，每個 subclass 有它自己的演算法，可以重複，可以不重複，所以如果把實作寫在 Context 很難 maintain
        *    <font color="#98c37a">**Cons**</font>
                1. Context 必須先對 Strategy 的實作清楚，才知道要挑選哪一個演算法作使用。
                2.  Strategy 和 Context 的通訊負擔變重，因為不論 ConcreteStrategy 裡面的實作是簡單還是複雜，都是共用 Strategy 介面的東西，可能一個簡單的 ConcreteStrategy 根本用不到那麼多，或是初始化的參數根本用不到，那你根本就不該用 Strategy Pattern。
        *    <font color="#98c37a">**Application**</font>
                **1. 定義所有的可以互相交換的演算法共同的 interface**
                **2. 把演算法的時做細節留給 subclass**
                **3. client 依賴 interface ，而不是具體實作**