定義一系列演算法,並將其封裝起來,使他們可以相互替換,演算法的變換不影響使用。
可以把策略想做成多種選項,依照適合的情況選擇適合的項目。比方說連假出遊,可以開車、坐高鐵、坐火車或坐飛機等選項可選。而在程式中也有,如排序法有冒泡排序、插入排序、選擇排序等等選擇。
策略模式通過對演算法的封裝,把使用演算法的責任以及演算法的實作給分開來,並委派不同的物件管理這些演算法。若使用if-else來判斷策略,若策略一多會讓程式變得複雜,在若要對新增刪除或更改演算法,就得異動到原始程式碼,違反了開閉原則。
name | description |
---|---|
Strategy(抽象策略) | 紀錄目前的內部狀態,提供建立備忘錄及回復備忘錄狀態的功能,可以訪問備忘錄內所有訊息。 |
ConcreteStrategy(實體策略) | 保存發起人的內部狀態,在需要的時候提供給發起人。 |
Context(環境) | 管理、保存及讀取備忘錄的功能,但不能對備忘錄的內容進行訪問及修改。 |
一開始有講到連假出遊,可以選擇開車、搭高鐵、坐飛機等不同的方式。而玩剪刀石頭布也是策略的一種,先將抽象策略建立起來,再分別建立剪刀石頭布的類別實作抽象策略。
enum Type { // 建立一個列舉方便等等程式使用
PAPER, SCISSORS, STONE
}
//抽象策略
interface Strategy {
public void strategyMethod(Type type); //策略方法
}
//實體策略:布
class Paper implements Strategy {
public void strategyMethod(Type type) {
switch(type){
case PAPER:
System.out.println("敵人出布,我出布 => 平手");
break;
case SCISSORS:
System.out.println("敵人出剪刀,我出布 => 輸");
break;
case STONE:
System.out.println("敵人出石頭,我出布 => 贏");
break;
}
}
}
//實體策略:剪刀
class Scissors implements Strategy {
public void strategyMethod(Type type) {
switch(type){
case PAPER:
System.out.println("敵人出布,我出剪刀 => 贏");
break;
case SCISSORS:
System.out.println("敵人出剪刀,我出剪刀 => 平手");
break;
case STONE:
System.out.println("敵人出石頭,我出剪刀 => 輸");
break;
}
}
}
//實體策略:石頭
class Stone implements Strategy {
public void strategyMethod(Type type) {
switch(type){
case PAPER:
System.out.println("敵人出布,我出石頭 => 輸");
break;
case SCISSORS:
System.out.println("敵人出剪刀,我出石頭 => 贏");
break;
case STONE:
System.out.println("敵人出石頭,我出石頭 => 平手");
break;
}
}
}
接著將環境建立起來,並在內部實作設定策略及執行策略的方法。
//環境
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void strategyMethod(Type type) {
strategy.strategyMethod(type);
}
}
最後我們用Scanner選擇猜拳要出什麼。
import java.util.*;
public class StrategyPattern {
public static void main(String[] args) {
Context c = new Context(); // 建立環境
Strategy paper = new Paper(); // 建立剪刀石頭布
Strategy scissors = new Scissors();
Strategy stone = new Stone();
Type[] arr = {Type.PAPER, Type.SCISSORS, Type.STONE};
Scanner scanner = new Scanner(System.in);
System.out.println("剪刀石頭布!!!");
Random ran = new Random();
while(true){
int num = ran.nextInt(3)+1;
System.out.println("請出拳(1:剪刀;2:石頭;3:布):");
int input = scanner.nextInt();
switch(input){ // 根據輸入不同的數字選擇不同策略
case 1:
c.setStrategy(scissors);
break;
case 2:
c.setStrategy(stone);
break;
case 3:
c.setStrategy(paper);
break;
}
c.strategyMethod(arr[num - 1]);
System.out.println("-----------------");
}
}
}
定義一系列演算法,並將其封裝起來,使他們可以相互替換,演算法的變換不影響使用。
Strategy(抽象策略):紀錄目前的內部狀態,提供建立備忘錄及回復備忘錄狀態的功能,可以訪問備忘錄內所有訊息。
ConcreteStrategy(實體策略):保存發起人的內部狀態,在需要的時候提供給發起人。
Context(環境):管理、保存及讀取備忘錄的功能,但不能對備忘錄的內容進行訪問及修改。
優點
1. 提供一系列可重複使用的演算法,適當的使用繼承可把共用程式碼轉移到父類別內,避免重複。
2. 提供相同行為不同實作方式,可以根據不同時間空間選擇適當的策略。
3. 在不修改原始程式碼的情況下,增加新的演算法,符合開閉原則。
4. 演算法的使用放到環境類別,演算法的實作放到具體類別,讓兩者分離。
缺點
1. Client必須了解所有演算法區別,才能適當使用。
2. 產生很多的策略類別。
1. 當系統需要動態的選擇策略時。
2. 一個類別定義多種行為,且這些行為在這個類別中有多個條件(if-else)語法形式出現時。
3. 系統演算法獨立,且要求對client隱藏實作細節時。