策略模式(Strategy Pattern)

策略模式(Strategy Pattern)

策略模式的定義:

  策略模式(Strategy Pattern)也叫做政策模式(Policy Pattern)其定義:定義一組算法,將他們封裝起來,使它們可以相互替換。


策略模式的優點:

  1. 算法直接可以相互替換。這是因爲策略都實現策略接口。

  2. 可以避免多重條件的情況出現。假設一個策略家族有N個成員,當一會需要策略A,另一會需要策略B,不使用策略模式的話,只能使用ifelse或switch語句實現,但是這樣的程序不容易維護,可讀性也比較差。使用策略模式後可以由其他模塊確定策略。

  3. 有良好的擴展性。增加一個策略,只需要實現策略接口,就這麼簡單。

 

策略模式的缺點

  1. 子類膨脹,一個策略一個類,策略類的數量比較驚人。

  2. 調用者必須知道所有具體策略的存在(調用一個策略還需要自己new出來)。 


策略模式的應用場景:

  1. 多個類只在算法或行爲稍有不同(在抽象概念上是相同的行爲)。

  2. 算法需要自由切換。 

 

策略模式的通用類圖:

 

Strategy(抽象策略角色):定義了每個算法必須有的算法和屬性。

StrategyA(具體策略角色):實現了抽象策略定義的接口。

Context(封裝角色):將策略封裝起來,屏蔽了調用者直接訪問具體策略。

 

策略模式的通用代碼:

抽象策略角色: 

public interface Strategy
{
public void operation();
}

具體策略角色:

複製代碼
public class StrategyA implements Strategy
{
@Override
public void operation()
{
System.out.println("strategyA");
}
}
複製代碼

封裝角色:

複製代碼
public class Context
{
private Strategy strategy = null;

public Context(Strategy strategy)
{
this.strategy = strategy;
}

/*實現策略可以相互替換*/
public void replaceStrategy(Strategy strategy)
{
this.strategy = strategy;
}

/*封裝角色提供接口,讓調用使用策略,屏蔽高層模塊直接使用策略*/
public void execute()
{
this.strategy.operation();
}
}
複製代碼

場景類:

複製代碼
public class Client
{
public static void main(String[] args)
{
/*這裏可以看出策略模式的缺點,調用者必須要知道調用的策略(自己new出來StrategyA的對象)*/
Context context = new Context(new StrategyA());
context.execute();

/*可以實現自由切換策略*/
context.replaceStrategy(new StrategyB());
context.execute();
}
}
複製代碼

 


舉個比較簡單的例子理解策略模式 

例子:"實現一個可以計算兩個整數加減乘除計算的功能"。

實現一:不使用策略模式

複製代碼
class Calculator
{
/*代表加減乘除的字符串,用於判斷使用什麼計算策略*/
private final static String ADD_SYMBOL = "+";
private final static String SUB_SYMBOL = "-";
private final static String MUL_SYMBOL = "*";
private final static String DIV_SYMBOL = "/";

/*策略選擇函數,operator字符串參數用於判斷,a、b兩個參數使用的計算策略*/
public int execute(int a,int b,String operator)
{
int result = 0;
if(ADD_SYMBOL.equals(operator))
{
result = this.add(a, b);
}
else if(SUB_SYMBOL.equals(operator))
{
result = this.sub(a, b);
}
else if(MUL_SYMBOL.equals(operator))
{
result = this.multi(a, b);
}
else if(DIV_SYMBOL.equals(operator))
{
result = this.div(a, b);
}

return result;
}

/*加法功能實現*/
private int add(int a,int b)
{
return a+b;
}

/*減法功能實現*/
private int sub(int a,int b)
{
return a-b;
}

/*乘法功能實現*/
private int multi(int a,int b)
{
return a*b;
}

/*除法功能實現*/
private int div(int a,int b)
{
//只是爲了說明問題,除數爲0就不考慮了
return a/b;
}
}
複製代碼

  這個實現比較容易想到,通過"operator"這個參數決定使用什麼計算策略。這樣的實現看上去完美的處理了需求,還是有很多需要改進的地方。首先,擴展性很差——若要添加一個求餘的需求,除了改源代碼旗本沒其他辦法了,添加一個需求改一次,這樣還了得。其次,可讀性不行,別看這個程序看上去還比較清晰,要是考慮上異常、"operator"檢查,功能變多後等等就不順眼了。最後,這個類職責不清晰,又要選擇策略又要實現策略。

實現二:使用策略模式

抽象策略角色(定義了每個策略都有的接口): 

public interface IStrategy
{
public int doAction(int a, int b);
}

具體策略角色(實現抽象策略):

複製代碼
/*實現加法策略*/
public class AddStrategy implements IStrategy
{
@Override
public int doAction(int a,int b)
{
return a+b;
}
}
複製代碼
複製代碼
/*實現除法策略*/
public class DivStrategy implements IStrategy
{
@Override
public int doAction(int a, int b)
{
return a/b;
}
}
複製代碼
複製代碼
/*實現乘法策略*/
public class MultiStrategy implements IStrategy
{
@Override
public int doAction(int a, int b)
{
return a*b;
}
}
複製代碼
複製代碼
/*實現減法策略*/
public class SubStrategy implements IStrategy
{
@Override
public int doAction(int a, int b)
{
return a-b;
}
}
複製代碼

封裝角色:

複製代碼
public class StrategyContext
{
private IStrategy strategy = null;

public StrategyContext(IStrategy strategy)
{
this.strategy = strategy;
}

public void replaceStrategy(IStrategy strategy)
{
this.strategy = strategy;
}

public int doAction(int a,int b)
{
return strategy.doAction(a, b);
}
}
複製代碼

場景類:

複製代碼
public class Client
{
public static void main(String[] args)
{
/*計算加法,策略模式自身缺點,必須知道具體策略(自己new出來AddStrategy對象)*/
StrategyContext context = new StrategyContext(new AddStrategy());
int result = context.doAction(2, 2);

System.out.println("result : "+result);
}
}
複製代碼

  這是按照常規的策略模式實現加減乘除功能的。這樣的實現基本上解決了第一種實現方式的缺點。增加一個功能,只需要實現接口 "IStrategy",擴展非常簡單便捷。if判斷語句消失了,可讀性大大提高。每個類的職責現在更清晰了。儘管如此,這個實現還是有一個非常大缺點,這個缺點是該模式自身的缺點,就是調用者必須知道具體策略(這個缺點可以使用混合模式解決)。
實現三:枚舉策略模式

複製代碼
enum EnumStrategy
{
/*加法策略的實現*/
ADD{
@Override
public int execute(int a, int b)
{
return a+b;
}
},

/*減法策略的實現*/
SUB{
@Override
public int execute(int a, int b)
{
return a-b;
}
},

/*乘法策略的實現*/
MUL{
@Override
public int execute(int a, int b)
{
return a*b;
}
},

/*除法策略的實現*/
DIV{
@Override
public int execute(int a, int b)
{
return a/b;
}
};

abstract public int execute(int a,int b);
}
複製代碼

來看看場景類:

複製代碼
public class EnumStrategyClient
{
public static void main(String[] args)
{
System.out.println(EnumStrategy.ADD.execute(2, 2));
}
}
複製代碼

  這種變形的策略模式真是太那個了(省略各種讚歎)....太精妙了,不得不佩服那些牛人,這樣的實現,讓可讀性提高到最高點了,一眼就能看明白。連調用都如此簡單(你說更看不懂了-_-!!那不是可讀性的問題,是你不理解枚舉,每一個枚舉值其實是這個枚舉類型的一個實例,它默認的前綴是public final static的,他其實和類差不多,只不過編譯器爲我們做了許多事情)。它擴展性沒有實現二好,這是受限於enum類型,所以這種變形可以運用在策略不易改變的地方。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章