---
# System prepended metadata

title: 程式設計
tags: [程式設計]

---

# 程式設計
###### tags: `程式設計`


## Design Pattern - Strategy
https://www.jyt0532.com/2017/09/12/template/
https://skyyen999.gitbooks.io/-study-design-pattern-in-java/content/strategy.html

Inheritance 繼承
Interface -> implements

#### 原本的設計：
有紅頭鴨與綠頭鴨inheritance鴨子。
但多了一隻鴨：黃色小鴨，繼承時，因為他不會叫，所以要override quack()，讓他不會叫。
但這只是其中一個例子，若不會叫的鴨某天多過會叫的鴨，那此程式就須大改...設計得不好。
![](https://i.imgur.com/iyCAuYH.png)

#### 開始改良：
- Design Pattern rule #1: Encapsulate what varies

把所有可能會更動的地方獨立出來 不要跟不會變動的地方混在一起。


- Design Pattern rule #2: Program to an interface, not an implementation

把會變動的method改成interface去寫
再由interface的subclass去實作method
現在就有兩個interface:


#### 改良後：
```java
//第一個interface 不一定每隻鴨都會叫
interface QuackBehavior {
    void quack();
}

//交給interface的subclass去實作quack的方法
class Quack implements QuackBehavior{
    @Override
    void quack() {
        System.out.println("qua~ qua~");
    }
}

class Mute implements QuackBehavior{
    @Override
    void quack() {
        System.out.println("...");
    }
}

//第二個interface 不一定每隻鴨都會飛
interface FlyBehavior {
    void fly();
}

class FlyWithWings implements FlyBehavior{
    void fly() {
        System.out.println("我飛我飛我飛飛飛");
    }
}
class FlyNoWay implements FlyBehavior{
    void fly() {
        System.out.println("我的翅膀不是長來飛的");
    }
}

//重頭戲Duck
class Duck {
    private QuackBehavior quackBehavior;
    private FlyBehavior flyBehavior;
    
    public setQuackBehavior(QuackBehavior q) {
        quackBehavior = q;
    }
    public setFlyBehavior(FlyBehavior f) {
        flyBehavior = f;
    }
    public void swim() {
        System.out.println("游R游RR");
    }
    public void display() {
        System.out.println("I'm a real duck");
    }
    public void performQuack() {
        quackBehavior.quack();
    }
    public void performFly() {
        flyBehavior.fly();
    }
}


//最後要繼承Duck時，只要分別在建構式宣告兩個interface即可有各種鴨的特性(飛/不飛、叫/不叫)
class RedheadDuck extends Duck{
    public RedheadDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    
    //因為RedHead和Duck只有這一個method做的事不一樣，所以override它一個就好
    public void display() {    
        System.out.println("I'm a real Redhead duck");
    }
}

```

![](https://i.imgur.com/nbrwT8a.png)

#### 還可以改鴨子的飛行方式，且不會影響到其他種類的鴨子
```java
RedheadDuck rh = new RedheadDuck();
rh.performFly(); // FlyWithWings的fly實作	
rh.setFlyBehavior(new FlyNoWay());
rh.performFly(); // FlyNoWay的fly實作
```

### Strategy pattern
* **定義演算法家族 並把每個演算法封裝起來 演算法之間彼此可以互換**
* **這個模式讓演算法的變動 不影響演算法的使用方式**
* **把抽象的方法抽離在interface 把實作留給interface的subclass**

#### Strategy 結構：
![](https://i.imgur.com/0YebuiR.png)


#### 優點
1. Strategy可以針對同一種行為有不同的實作 
1. Strategy interface還可以玩自己的hierarchy遊戲 繼承的機制可以把共有的功能提出來
1. 對於Context來說 每個subclass有他自己的演算法(可能和其他人重複 可能不重複) 如果把演算法實作寫在Context裡會很難maintain

#### 缺點
1. Context必須對Strategy的實作清楚 才知道要挑哪一種演算法來用
1. Strategy和Cotext的通訊負擔變重 因為不論ConcreteStrategy裡實作起來簡單還是複雜 都是共用Strategy介面 Strategy介面的某些東西 可能一個簡單的ConcreteStrategy根本不需要 或是Context給它的初始化參數它用不到 這種情況也許你不該用Strategy pattern




## Design Pattern - Template

#### 最初的設計：
泡茶的步驟如下: 
1.把水煮開 
2.沸水倒進茶葉
3.茶倒進杯子
4.加檸檬

泡咖啡的步驟如下:
1.把水煮開
2.用沸水沖咖啡
3.咖啡倒進杯子
4.加糖和牛奶

發現step.1和3根本一樣

#### 改良後：

```java
//用抽象類別！！
public abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew();
    //步驟2 steepTeaBag, brewCoffeeGrinds

    abstract void addCondiments();
    //步驟4 addLemon addSugarAndMilk

    void boilWater() {//步驟1
        System.out.println("Boiling water");
    }

    void pourInCup() {//步驟3
        System.out.println("Pouring into cup");
    }
}

//Tea只要override brew()和addCondiments()即可
class Tea extends CaffeineBeverage {
    public void brew() {
        System.out.println("Steeping the tea");
    }
    public void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

//Coffee同上
class Coffee extends CaffeineBeverage {
    public void brew() {
        System.out.println("Dripping Coffee through filter");
    }
    public void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}
```

如果今天又要泡牛奶，牛奶不用加料addCondiments()
那可在prepareRecipe加上hook，去控制是否要加料

```java
public abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        //新增這個
        if (wantsCondiments()){
            addCondiments();
        }
    }
    
    ...省略
    
    //新增這個
    boolean wantsCondiments() {
        return true;
    }
}

class Milk extends CaffeineBeverage {
    public void brew() {
        System.out.println("Dripping Coffee through filter");
    }
    
    //因為是抽象方法，所以一定要實作，把它改成啥都沒差，因為wantsCondiments會控制不加料
    public void addCondiments() { }
    
    public boolean wantsCondiments() {
        return false;
    }
}
```

#### Q: 奇怪你這不是多此一舉 你的牛奶直接implement addCondiments讓他不做任何事不就好了嗎
A: 那是剛好現在這個例子裡 addCondiments函數是抽象函數(父類別希望你實作的函數) 如果今天去if block裡面的是boilWater或是pourInCup 那就不能像你那樣搞

#### Q: 在你牛奶的case 你又override抽象函數 又override掛鉤 那我身為抽象父類別 到底該什麼時候用抽象函數 什麼時候用掛勾呢

A: 當你希望子類別一定要實作的函數 就用抽象函數 可以選擇要不要實作的函數 就用掛鉤


#### Tempelete method 結構
將一個演算法的骨架定義在一個方法中
而演算法本身用到的(一部分的)方法則定義在次類別中
Tempelete method讓次類別在不改變演算法骨架的前提下
重新定義演算法中的某些步驟
![](https://i.imgur.com/HLooDlV.png)

#### 優缺點
1. 由父類別主導演算法 而不是各個subclass各自maintain
2. 程式碼再利用
3. 演算法只存在一個地方

### 差異
#### Strategy
讓你能夠在run time對於同一個function選擇完全不同的策略(演算法) 不同的策略之間彼此獨立不相關

(要選擇怎麼fly，可以立馬呼叫setFlyBehavior做修改，改完後，一樣呼叫performfly()就可飛了，但會依照新的Behavior飛)
#### Template method
預先定義好了演算法的每個步驟
某些步驟是固定的
某些步驟是彈性的
彈性的步驟交給subclass去實作

(泡咖啡的步驟都在prepare定好了，brew和addCondiment和wantsCondiments都由subclass實作)




## 4 簡述Template、Strategy pattern，並說明他們之間的差異
#### Template:
以抽象類別為基礎，把演算法都放在一個方法中，讓次類別在不改動方法的情況下重新定義某部分的步驟。
```java=
class abstract Teacher {
	final void do() {
		work();
		if (isFullTimeTeacher()) {		//hook
			calculateFullTimeSalary();
		} else {
			calculatePartTimeSalary();
		}
		calculateEfficiency();
    }
    
    void work() {
        System.out.println("do work");
    }
    
    void calculateFullTimeSalary(int month) {
        System.out.println(String.valueOf(month*40000));
    }
    
    void calculatePartTimeSalary(int wage ,int hour) {
        System.out.println(String.valueOf(wage*hour));
    }
    
    boolean isFullTimeTeacher() {
        return true;
    }
    
    abstract void calculateEfficiency();
    }
    
    class FullTimeTeacher extends Teacher {
        public void calculateEfficiency() {
        System.out.println("calculate full-time teacher's efficiency.");
    }
}
```
#### Strategy pattern:
定義父類別時，將子類別不一定會使用到的method放入interface，再交由subclass實作。父類別內就以interface的方式增加彈性，開放各種子類別選擇的空間。沿用上面DIP的例子，當有另一class繼承Teacher，可任意選擇managable的權限，且不用改動原本的程式碼。
```java=
Class PartTimeTeacher extends Teacher{
	PartTimeTeacher() {
		Managable = new Score();
	}
}
```

#### 差異：
Template是已經定義好演算法的步驟，若某步驟有不同的選擇，那就交由subclass去定義。
Strategy可以讓我們在run time上選擇不同的策略(setManagable)，呼叫同一個method時(managed)出現不同的效果。





<style>

h2{
    color: #65A287;
}
h4{
    color: #875244;
}
li:first-line{
    color: #6699BA;
}
</style>