該模式最常見的應(yīng)用場景是,利用它來避免冗長的 if-else 或 switch 分支判斷。不過,它的作用還不止如此。它也可以像模板模式那樣,提供框架的擴展點等等。
1) 原理和實現(xiàn)
策略模式,英文全稱是 Strategy Design Pattern。該模式是這樣定義的:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it。
翻譯成中文就是:定義一族算法類,將每個算法分別封裝起來,讓它們可以互相替換。策略模式可以使算法的變化獨立于使用它們的客戶端(這里的客戶端代指使用算法的代碼)。
策略模式主要包含以下角色:
- 策略接口(Strategy):定義所有支持的算法的公共接口。客戶端使用這個接口與具體策略進行交互。
- 具體策略(Concrete Strategy):實現(xiàn)策略接口的具體策略類。這些類封裝了實際的算法邏輯。
- 上下文(Context):持有一個策略對象,用于與客戶端進行交互。上下文可以定義一些接口,讓客戶端不直接與策略接口交互,從而實現(xiàn)策略的封裝。
讓我們以一個簡單的例子來說明策略模式:假設(shè)我們要實現(xiàn)一個計算器,支持加法、減法和乘法運算。我們可以使用策略模式將各種運算獨立為不同的策略,并讓客戶端根據(jù)需要選擇和使用不同的策略。
首先,定義一個策略接口Operation
:
public interface Operation {
double execute(double num1, double num2);
}
接下來,創(chuàng)建具體策略類來實現(xiàn)加法、減法和乘法運算:
public class Addition implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 + num2;
}
}
public class Subtraction implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 - num2;
}
}
public class Multiplication implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 * num2;
}
}
然后,創(chuàng)建一個上下文類Calculator
,讓客戶端可以使用這個類來執(zhí)行不同的運算:
public class Calculator {
// 客戶端可以持有一個算法
private Operation operation;
public void setOperation(Operation operation) {
this.operation = operation;
}
public double calculate(double num1, double num2) {
return operation.execute(num1, num2);
}
}
現(xiàn)在,客戶端可以使用Calculator
類來執(zhí)行不同的運算,例如:
public class Client {
public static void main(String[] args) {
// 定義一個調(diào)用方
Calculator calculator = new Calculator();
// 計算加法
Operation operation = new Addition();
calculator.setOperation(operation);
double calculate = calculator.calculate(10, 12.9);
System.out.println("calculate = " + calculate);
// 計算減法
operation = new Subtraction();
calculator.setOperation(operation);
calculate = calculator.calculate(10, 12.9);
System.out.println("calculate = " + calculate);
}
}
在這個例子中,我們使用策略模式將加法、減法和乘法運算獨立為不同的策略。客戶端可以根據(jù)需要選擇和使用不同的策略。Calculator
上下文類持有一個Operation
策略對象,并通過setOperation
方法允許客戶端設(shè)置所需的策略。這種方式使得算法的選擇和執(zhí)行更加靈活,易于擴展和維護。
策略模式的優(yōu)點包括:
- 提高代碼的可維護性和可擴展性。當需要添加新的算法時,我們只需要實現(xiàn)一個新的具體策略類,而無需修改客戶端代碼。
- 符合開閉原則。策略模式允許我們在不修改現(xiàn)有代碼的情況下引入新的策略。
- 避免使用多重條件判斷。使用策略模式可以消除一些復雜的條件判斷語句,使代碼更加清晰和易于理解。
策略模式的缺點包括:
- 客戶端需要了解所有的策略。為了選擇合適的策略,客戶端需要了解不同策略之間的區(qū)別。
- 增加了類的數(shù)量。策略模式會導致程序中具體策略類的數(shù)量增加,這可能會導致代碼的復雜性增加。
在實際開發(fā)中,我們可以根據(jù)業(yè)務(wù)需求和系統(tǒng)架構(gòu)靈活地運用策略模式。例如,在電商系統(tǒng)中,我們可以使用策略模式處理不同的促銷策略;在游戲系統(tǒng)中,我們可以使用策略模式處理不同的角色行為等。
1、策略的定義
策略類的定義比較簡單,包含一個策略接口和一組實現(xiàn)這個接口的策略類。因為所有的策略類都實現(xiàn)相同的接口,所以,客戶端代碼基于接口而非實現(xiàn)編程,可以靈活地替換不同的策略。示例代碼如下所示:
public interface Strategy {
void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
//具體的算法...
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
//具體的算法...
}
}
2、策略的創(chuàng)建
因為策略模式會包含一組策略,在使用它們的時候,一般會通過類型(type)來判斷創(chuàng)建哪個策略來使用。為了封裝創(chuàng)建邏輯,我們需要對客戶端代碼屏蔽創(chuàng)建細節(jié)。
事實上我們可以做一定的優(yōu)化,可以把根據(jù) type 創(chuàng)建策略的邏輯抽離出來,放到工廠類中。示例代碼如下所示:
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return strategies.get(type);
}
}
一般來講,如果策略類是無狀態(tài)的,不包含成員變量,只是純粹的算法實現(xiàn),這樣的策略對象是可以被共享使用的,不需要在每次調(diào)用 getStrategy() 的時候,都創(chuàng)建一個新的策略對象。針對這種情況,我們可以使用上面這種工廠類的實現(xiàn)方式,事先創(chuàng)建好每個策略對象,緩存到工廠類中,用的時候直接返回。
相反,如果策略類是有狀態(tài)的,根據(jù)業(yè)務(wù)場景的需要,我們希望每次從工廠方法中,獲得的都是新創(chuàng)建的策略對象,而不是緩存好可共享的策略對象,那我們就需要按照如下方式來實現(xiàn)策略工廠類。
public class StrategyFactory {
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
if (type.equals("A")) {
return new ConcreteStrategyA();
} else if (type.equals("B")) {
return new ConcreteStrategyB();
}
return null;
}
}
3、策略的使用
剛剛講了策略的定義和創(chuàng)建,現(xiàn)在,我們再來看一下,策略的使用。
我們知道,策略模式包含一組可選策略,客戶端代碼一般如何確定使用哪個策略呢?最常見的是運行時動態(tài)確定使用哪種策略,這也是策略模式最典型的應(yīng)用場景。
這里的“運行時動態(tài)”指的是,我們事先并不知道會使用哪個策略,而是在程序運行期間,根據(jù)配置、用戶輸入、計算結(jié)果等這些不確定因素,動態(tài)決定使用哪種策略。接下來,我們通過一個例子來解釋一下。文章來源:http://www.zghlxwxcb.cn/news/detail-828205.html
// 策略接口:EvictionStrategy
// 策略類:LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy...
// 策略工廠:EvictionStrategyFactory
public class UserCache {
private Map<String, User> cacheData = new HashMap<>();
private EvictionStrategy eviction;
public UserCache(EvictionStrategy eviction) {
this.eviction = eviction;
}
//...
}
// 運行時動態(tài)確定,根據(jù)配置文件的配置決定使用哪種策略
public class Application {
public static void main(String[] args) throws Exception {
EvictionStrategy evictionStrategy = null;
Properties props = new Properties();
props.load(new FileInputStream("./config.properties"));
String type = props.getProperty("eviction_type");
evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type);
UserCache userCache = new UserCache(evictionStrategy);
//...
}
}
// 非運行時動態(tài)確定,在代碼中指定使用哪種策略
public class Application {
public static void main(String[] args) {
//...
EvictionStrategy evictionStrategy = new LruEvictionStrategy();
UserCache userCache = new UserCache(evictionStrategy);
//...
}
}
從上面的代碼中,我們也可以看出,“非運行時動態(tài)確定”,也就是第二個 Application 中的使用方式,并不能發(fā)揮策略模式的優(yōu)勢。在這種應(yīng)用場景下,策略模式實際上退化成了“面向?qū)ο蟮亩鄳B(tài)特性”或“基于接口而非實現(xiàn)編程原則”,這其實就是開篇時列舉的例子的場景。文章來源地址http://www.zghlxwxcb.cn/news/detail-828205.html
到了這里,關(guān)于設(shè)計模式-策略模式 Strategy的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!