51CTO讀者成長(zhǎng)計(jì)劃社群招募,咨詢小助手(微信號(hào):CTOjishuzhan)
作者丨Ashutosh Krishna
譯者丨張哲剛
審校丨重樓
策略設(shè)計(jì)模式是一種行為設(shè)計(jì)模式。利用它,你可以通過將對(duì)象封裝到不同的策略中,進(jìn)而動(dòng)態(tài)地更改對(duì)象的行為。
這種模式可以使對(duì)象在運(yùn)行時(shí)能夠從多個(gè)算法和行為中進(jìn)行選擇,而不限于僅僅靜態(tài)地選擇單個(gè)算法和行為。
策略設(shè)計(jì)模式是基于組合而不是繼承的原則。它定義了一系列算法,封裝了每個(gè)算法,它們?cè)谶\(yùn)行時(shí)可以互相轉(zhuǎn)換。策略設(shè)計(jì)模式的核心思想是將算法與主對(duì)象分開。它允許對(duì)象將算法的行為委托給所設(shè)計(jì)的眾多策略中的一個(gè)。
一言而蔽之,策略設(shè)計(jì)模式提供了一種方法,這種方法可以將對(duì)象的行為提取到可以在運(yùn)行時(shí)切換出入的單獨(dú)類中。從而使對(duì)象具有更好的靈活性和重用性,因?yàn)檫@種模式可以輕易地添加或修改不同的策略,而無需更改對(duì)象的核心代碼。
一、使用策略設(shè)計(jì)模式的好處
使用策略設(shè)計(jì)模式有很多便利之處,具體如下:
1、更好的代碼靈活性:通過將對(duì)象的行為封裝到不同的策略中,使得代碼更加靈活且更易于修改。
2、更好的代碼可重用性:由于這些策略是可封裝以及可以互換的,所以它們可以在不同的對(duì)象和項(xiàng)目中重復(fù)使用。
3、促使更好的編碼實(shí)踐:策略設(shè)計(jì)模式可以促使良好的編碼實(shí)踐,養(yǎng)成良好的編碼習(xí)慣,例如關(guān)注點(diǎn)分離以及代碼復(fù)雜性的降低。
4、簡(jiǎn)化測(cè)試:策略設(shè)計(jì)模式通過將算法和行為與對(duì)象分離,從而使測(cè)試得到簡(jiǎn)化。
二、策略設(shè)計(jì)模式的實(shí)例
策略設(shè)計(jì)模式可以應(yīng)用于各種場(chǎng)景,典型的例子如下:
1、排序算法:可以將不同的排序算法封裝到單獨(dú)的策略中,并發(fā)送給需要排序的對(duì)象。
2、驗(yàn)證規(guī)則:可以將不同的驗(yàn)證規(guī)則封裝到單獨(dú)的策略中,并發(fā)送給需要驗(yàn)證的對(duì)象。
3、文本格式:可以將不同的文本格式設(shè)置策略封裝到單獨(dú)的策略中,并發(fā)送給需要格式化的對(duì)象。
4、數(shù)據(jù)庫(kù)訪問:可以將不同的數(shù)據(jù)庫(kù)訪問策略封裝到單獨(dú)的策略中,并發(fā)送給需要從不同的數(shù)據(jù)源訪問數(shù)據(jù)的對(duì)象。
5、支付策略:可以將不同的支付方式封裝到單獨(dú)的策略中,并發(fā)送給需要處理支付的對(duì)象。
三、了解策略設(shè)計(jì)模式
策略設(shè)計(jì)模式是面向?qū)ο蟮木幊填I(lǐng)域里一個(gè)非常卓越的模式。它提供了一種靈活的方法來封裝和交換對(duì)象在運(yùn)行時(shí)的行為,從而使代碼更具適應(yīng)性且更易于維護(hù)。
在本節(jié)中,我們將更深入地探討策略設(shè)計(jì)模式,研究其定義、組件及其工作原理。
1.策略設(shè)計(jì)模式的組成部分
策略設(shè)計(jì)模式主要包括三項(xiàng)重要組件:
- 上下文:策略設(shè)計(jì)模式所包含的策略行為委托對(duì)象。上下文負(fù)責(zé)維護(hù)對(duì)策略對(duì)象的引用,并通過公共接口與其進(jìn)行交互。
- 策略接口:此接口定義所有策略的行為。策略通過此接口來實(shí)現(xiàn)其定義的各種行為。
- 具體策略:實(shí)現(xiàn)策略接口的類。每個(gè)策略都封裝了一個(gè)特定的行為,上下文可以在運(yùn)行時(shí)切換到該行為。
2.策略設(shè)計(jì)模式的工作原理
策略設(shè)計(jì)模式的工作原理,是將對(duì)象的行為與對(duì)象本身分開。行為封裝到不同的策略中,每個(gè)策略都有自己特定的行為實(shí)施。
上下文維護(hù)對(duì)策略對(duì)象的引用,并通過公共接口與其進(jìn)行交互。在運(yùn)行進(jìn)程中,上下文可以將當(dāng)前策略與另外的策略進(jìn)行交換,從而有效達(dá)到更改對(duì)象行為的目的。
3.策略設(shè)計(jì)模式的實(shí)際應(yīng)用舉例
音樂流媒體服務(wù)是策略設(shè)計(jì)模式的一個(gè)應(yīng)用實(shí)例。這項(xiàng)服務(wù)里,不同的訂閱層次有其各自不同的定價(jià)模型。
針對(duì)每個(gè)訂閱層次,可以用不同的定價(jià)策略來封裝其單獨(dú)的價(jià)格體系。該服務(wù)的計(jì)費(fèi)系統(tǒng)會(huì)將定價(jià)計(jì)算這一行為事項(xiàng)委托給當(dāng)前訂閱層次對(duì)應(yīng)的策略,從而可以輕松修改和擴(kuò)展價(jià)格體系。
支付策略是另外一個(gè)應(yīng)用實(shí)例。不同的支付方式可以封裝成單獨(dú)的策略,每種策略都具有它自身單獨(dú)的處理邏輯。
購(gòu)物車應(yīng)用程序可以使用策略設(shè)計(jì)模式將信用卡、PayPal和加密貨幣支付方法封裝到單獨(dú)策略中,它們可以在運(yùn)行時(shí)交換。應(yīng)用程序的支付處理系統(tǒng)將支付方式委托給當(dāng)前制定的策略,從而可以輕松修改和擴(kuò)展支付方式。
四、如何實(shí)現(xiàn)策略設(shè)計(jì)模式
在本節(jié)中,我們將討論如何實(shí)現(xiàn)策略設(shè)計(jì)模式。我們將從一個(gè)不符合策略設(shè)計(jì)模式的代碼示例開始,找到和研究它存在的問題。然后,我們?cè)僦貥?gòu)代碼,來演示如何實(shí)現(xiàn)策略設(shè)計(jì)模式。
要在Java中實(shí)現(xiàn)策略設(shè)計(jì)模式,需要執(zhí)行以下步驟:
- 確定需要封裝的算法或行為,并使其可互換。
- 定義一個(gè)表示行為的接口,使用單一方法簽名來接收所有必需的參數(shù)。
- 實(shí)施一個(gè)具體類,用來提供接口中所定義行為的特定實(shí)現(xiàn)。
- 定義一個(gè)上下文類,該上下文類保持對(duì)接口的引用并在需要時(shí)調(diào)用其方法。
- 修改上下文類,以允許動(dòng)態(tài)交換在運(yùn)行時(shí)得以具體實(shí)現(xiàn)。
1.代碼示例
我們來看以下代碼:
packagewithoutstrategy;publicclassPaymentProcessor{
privatePaymentTypepaymentType;
publicvoidprocessPayment(doubleamount){
if(paymentType==PaymentType.CREDIT_CARD){
System.out.println("Processing
credit card payment of amount "+amount);
}elseif(paymentType==PaymentType.DEBIT_CARD){
System.out.println("Processing
debit card payment of amount "+amount);
}elseif(paymentType==PaymentType.PAYPAL){
System.out.println("Processing
PayPal payment of amount "+amount);
}else{
thrownewIllegalArgumentException("Invalid
payment type");
}
}
publicvoidsetPaymentType(PaymentTypepaymentType){
this.paymentType=paymentType;}}enumPaymentType{
CREDIT_CARD,
DEBIT_CARD,
PAYPAL}
支付處理器.java
在此代碼中,支付處理器類具有一個(gè)用來接受付款金額并處理付款的方法。付款類型是使用“設(shè)置付款類型”方法設(shè)置并設(shè)置“付款類型”字段。然后,該方法檢查其值并處理相應(yīng)的付款。
此代碼的問題在于它違反了開-閉(開放封閉)原則,該原則指明類應(yīng)該面向擴(kuò)展開放,但必須面向修改封閉。而在此代碼中,如果想要添加新的付款類型,則必須修改方法,這就違反了開-閉原則。
該支付處理器類通過使用條件語(yǔ)句來確定付款類型,然后相應(yīng)地對(duì)其進(jìn)行處理,從而違反了策略模式。隨著付款類型數(shù)量的增加,這種方法很快就會(huì)變得難以管理和不夠靈活。
要解決這個(gè)問題,可以使用策略設(shè)計(jì)模式。首先,為所有支付策略定義一個(gè)通用接口,在本例中為支付策略接口:
package withstrategy;
public interface PaymentStrategy {
void processPayment(double amount);
}
支付策略.java
然后,你可以為每種付款類型定義其支付策略接口的具體實(shí)現(xiàn)。例如下面這些類:
- 信用卡支付策略;
- 借記卡支付策略;
- 貝寶支付策略。
package withstrategy;
public class CreditCardPaymentStrategy implements PaymentStrategy {
public void processPayment(double amount) {
System.out.println("Processing credit card payment of amount " + amount);
}
}
信用卡支付策略.java
package withstrategy;
public class DebitCardPaymentStrategy implements PaymentStrategy {
public void processPayment(double amount) {
System.out.println("Processing debit card payment of amount " + amount);
}
}
借記卡支付策略.java
package withstrategy;
public class PaypalPaymentStrategy implements PaymentStrategy {
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of amount " + amount);
}
}
貝寶支付策略.java
最后,更新支付處理器類用以從構(gòu)造器中獲取支付策略對(duì)象,用于處理付款流程:
package withstrategy;
public class PaymentProcessor {
private PaymentStrategy paymentStrategy;
public PaymentProcessor(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(double amount) {
paymentStrategy.processPayment(amount);
}
}
支付處理器.java
這一實(shí)現(xiàn)遵循開閉原則和策略模式,在其中你可以通過創(chuàng)建支付策略接口的新實(shí)現(xiàn)來添加新的支付類型,而并不需要修改現(xiàn)有代碼。
2.實(shí)施策略設(shè)計(jì)模式的最佳方案
下面是實(shí)施策略設(shè)計(jì)模式時(shí),要力爭(zhēng)記牢的一些最佳方案:
- 保持界面簡(jiǎn)潔,專注于單一職責(zé)。
- 將任何有狀態(tài)的行為封裝在具體策略類中,而不是在上下文類中。
- 使用依賴關(guān)系注入將具體策略傳達(dá)給上下文類,而不是直接在上下文類中創(chuàng)建它。
- 使用枚舉或工廠類為創(chuàng)建和管理具體策略對(duì)象提供集中位置。
五、策略設(shè)計(jì)模式的實(shí)際應(yīng)用
策略設(shè)計(jì)模式已經(jīng)廣泛用于各種實(shí)際應(yīng)用。Java集合框架就是一個(gè)鮮明的例子。集合框架提供了一組接口和類來表示對(duì)象的集合,例如列表、集和映射。依據(jù)集合的具體行為,該框架允許將不同的策略應(yīng)用于集合。
例如,集合框架包含一個(gè)名為“分類”的方法,用于對(duì)集合進(jìn)行排序。該方法將比較器對(duì)象作為參數(shù),負(fù)責(zé)比較集合中的對(duì)象。比較器接口定義了用于比較兩個(gè)對(duì)象的策略,“分類”方法使用此策略對(duì)集合進(jìn)行排序。
此外,集合框架還包括迭代器接口,該接口定義了訪問集合元素的策略。迭代器允許用戶遍歷集合,而不會(huì)公開其內(nèi)部結(jié)構(gòu),該結(jié)構(gòu)可能會(huì)隨時(shí)間而變化。通過使用迭代器接口,用戶可以在訪問集合元素的不同策略之間切換。
六、總結(jié)
在本教程中,我們探討了策略設(shè)計(jì)模式及其在Java中的實(shí)現(xiàn)。我們已經(jīng)了解了如何使用策略模式將對(duì)象的行為與其實(shí)現(xiàn)分開,從而使代碼具備更大的靈活性和可維護(hù)性。
我們討論了策略設(shè)計(jì)模式的組件,包括上下文、策略接口和具體策略。我們還提供了一個(gè)示例,用以說明如何使用該模式來實(shí)現(xiàn)支付系統(tǒng)的靈活性,可以在單個(gè)界面實(shí)現(xiàn)多種支付選項(xiàng)。
通過將對(duì)象的行為與其實(shí)現(xiàn)分離,策略模式提供了足夠的靈活性,更能夠適應(yīng)日新月異不斷變化的實(shí)際需求。
譯者介紹:
張哲剛,51CTO社區(qū)編輯,系統(tǒng)運(yùn)維工程師,國(guó)內(nèi)較早一批硬件評(píng)測(cè)及互聯(lián)網(wǎng)從業(yè)者,曾入職阿里巴巴。
原文鏈接:https://www.freecodecamp.org/news/a-beginners-guide-to-the-strategy-design-pattern/