策略模式是什么
策略模式的用意是針對(duì)一組算法烙丛,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中跳纳,從而使得它們可以相互替換疟暖。策略模式使得算法可以在不影響到客戶端的情況下發(fā)生變化紊搪。
使用策略模式可以把行為和環(huán)境分割開來蜜葱。環(huán)境類負(fù)責(zé)維持和查詢行為類,各種算法則在具體策略類(ConcreteStrategy)中提供耀石。由于算法和環(huán)境獨(dú)立開來牵囤,算法的增減、修改都不會(huì)影響環(huán)境和客戶端。當(dāng)出現(xiàn)新的促銷折扣或現(xiàn)有的折扣政策出現(xiàn)變化時(shí)揭鳞,只需要實(shí)現(xiàn)新的策略類炕贵,并在客戶端登記即可。策略模式相當(dāng)于"可插入式(Pluggable)的算法"野崇。
策略模式的結(jié)構(gòu)
策略模式是對(duì)算法的包裝称开,是把使用算法的責(zé)任和算法本身分割開,委派給不同的對(duì)象管理乓梨。策略模式通常把一個(gè)系列的算法包裝到一系列的策略類里面鳖轰,作為一個(gè)抽象策略類的子類。用一句話來說督禽,就是:"準(zhǔn)備一組算法脆霎,并將每一個(gè)算法封裝起來,使得它們可以互換狈惫。"
這個(gè)模式涉及到三個(gè)角色:
-
環(huán)境(Context)角色
:持有一個(gè)Strategy類的引用睛蛛。 -
抽象策略(Strategy)角色
:這是一個(gè)抽象角色,通常由一個(gè)接口或抽象類實(shí)現(xiàn)胧谈。此角色給出所有的具體策略類所需的接口忆肾。 -
具體策略(ConcreteStrategy)角色
:包裝了相關(guān)的算法或行為。
java示例代碼
import java.util.ArrayList;
import java.util.List;
public class StrategyPatternWiki {
public static void main(final String[] arguments) {
Customer firstCustomer = new Customer(new NormalStrategy());
// Normal billing
firstCustomer.add(1.0, 1);
// Start Happy Hour
firstCustomer.setStrategy(new HappyHourStrategy());
firstCustomer.add(1.0, 2);
// New Customer
Customer secondCustomer = new Customer(new HappyHourStrategy());
secondCustomer.add(0.8, 1);
// The Customer pays
firstCustomer.printBill();
// End Happy Hour
secondCustomer.setStrategy(new NormalStrategy());
secondCustomer.add(1.3, 2);
secondCustomer.add(2.5, 1);
secondCustomer.printBill();
}
}
class Customer {
private List<Double> drinks;
private BillingStrategy strategy;
public Customer(final BillingStrategy strategy) {
this.drinks = new ArrayList<Double>();
this.strategy = strategy;
}
public void add(final double price, final int quantity) {
drinks.add(strategy.getActPrice(price*quantity));
}
// Payment of bill
public void printBill() {
double sum = 0;
for (Double i : drinks) {
sum += i;
}
System.out.println("Total due: " + sum);
drinks.clear();
}
// Set Strategy
public void setStrategy(final BillingStrategy strategy) {
this.strategy = strategy;
}
}
interface BillingStrategy {
double getActPrice(final double rawPrice);
}
// Normal billing strategy (unchanged price)
class NormalStrategy implements BillingStrategy {
@Override
public double getActPrice(final double rawPrice) {
return rawPrice;
}
}
// Strategy for Happy hour (50% discount)
class HappyHourStrategy implements BillingStrategy {
@Override
public double getActPrice(final double rawPrice) {
return rawPrice*0.5;
}
}
策略模式的使用場景
在下面的情況下應(yīng)當(dāng)考慮使用策略模式:
1菱肖、如果在一個(gè)系統(tǒng)里面有許多類客冈,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動(dòng)態(tài)地讓一個(gè)對(duì)象在許多行為中選擇一種行為稳强。
2场仲、一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種。那么這些算法可以包裝到一個(gè)個(gè)的具體算法類里面退疫,而這些具體算法類都是一個(gè)抽象算法類的子類渠缕。換言之,這些具體算法類均有統(tǒng)一的接口褒繁,由于多態(tài)性原則亦鳞,客戶端可以選擇使用任何一個(gè)具體算法類,并只持有一個(gè)數(shù)據(jù)類型是抽象算法類的對(duì)象棒坏。
3燕差、一個(gè)系統(tǒng)的算法使用的數(shù)據(jù)不可以讓客戶端知道。策略模式可以避免讓客戶端涉及到不必要接觸到的復(fù)雜的和只與算法有關(guān)的數(shù)據(jù)坝冕。
4徒探、如果一個(gè)對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J轿箍撸@些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)刹帕。此時(shí)吵血,使用策略模式,把這些行為轉(zhuǎn)移到相應(yīng)的具體策略類里面偷溺,就可以避免使用難以維護(hù)的多重條件選擇語句,并體現(xiàn)面向?qū)ο笤O(shè)計(jì)的概念钱贯。
策略模式的優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn)有:
1挫掏、 策略模式提供了管理相關(guān)的算法族的辦法。策略類的等級(jí)結(jié)構(gòu)定義了一個(gè)算法或行為族秩命。恰當(dāng)使用繼承可以把公共的代碼移到父類里面尉共,從而避免重復(fù)的代碼。
2弃锐、策略模式提供了可以替換繼承關(guān)系的辦法袄友。繼承可以處理多種算法或行為。如果不是用策略模式霹菊,那么使用算法或行為的環(huán)境類就可能會(huì)有一些子類剧蚣,每一個(gè)子類提供一個(gè)不同的算法或行為。但是旋廷,這樣一來算法或行為的使用者就和算法或行為本身混在一起。決定使用哪一種算法或采取哪一種行為的邏輯就和算法或行為的邏輯混合在一起,從而不可能再獨(dú)立演化葫掉。繼承使得動(dòng)態(tài)改變算法或行為變得不可能拓哺。
3、使用策略模式可以避免使用多重條件轉(zhuǎn)移語句扎运。多重轉(zhuǎn)移語句不易維護(hù)瑟曲,它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起,統(tǒng)統(tǒng)列在一個(gè)多重轉(zhuǎn)移語句里面豪治,比使用繼承的辦法還要原始和落后洞拨。
缺點(diǎn)有:
1、客戶端必須知道所有的策略類鬼吵,并自行決定使用哪一個(gè)策略類扣甲。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時(shí)選擇恰當(dāng)?shù)乃惴惓菀巍Q言之琉挖,策略模式只適用于客戶端知道所有的算法或行為的情況。
2涣脚、策略模式造成很多的策略類示辈。有時(shí)候可以通過把依賴于環(huán)境的狀態(tài)保存到客戶端里面,而將策略類設(shè)計(jì)成可共享的遣蚀,這樣策略類實(shí)例可以被不同客戶端使用矾麻。換言之纱耻,可以使用享元模式來減少對(duì)象的數(shù)量。