設(shè)計(jì)模式之策略模式(Strategy Pattern)

策略模式定義

策略模式(Strategy Pattern)是一種行為型設(shè)計(jì)模式,定義了一系列算法,并將每個(gè)算法封裝起來,使它們可以相互替換。策略模式使得算法可以在不影響客戶端的情況下發(fā)生變化,其主要目的是將算法的實(shí)現(xiàn)與使用解耦,提高程序的靈活性和可擴(kuò)展性。

基本概念

  • 策略(Strategy):定義了算法的通用接口,各種具體策略類實(shí)現(xiàn)該接口。
  • 具體策略(Concrete Strategy):實(shí)現(xiàn)了策略接口,封裝了具體的算法或行為。
  • 上下文(Context):持有一個(gè)策略的引用,客戶端通過上下文來與策略交互。

策略模式的種類

雖然策略模式本身沒有明顯的種類劃分,但根據(jù)實(shí)現(xiàn)方式和應(yīng)用場景的不同,可以有以下變體:

  1. 經(jīng)典策略模式:使用接口或抽象類定義策略,具體策略類實(shí)現(xiàn)該接口,客戶端通過上下文來使用策略。
  2. 匿名內(nèi)部類或 Lambda 表達(dá)式:在支持函數(shù)式編程的語言中,可以使用匿名類或 Lambda 表達(dá)式簡化策略的實(shí)現(xiàn)。
  3. 策略枚舉:當(dāng)策略數(shù)量固定時(shí),可以使用枚舉類型來實(shí)現(xiàn)不同的策略。

基本上使用1、3兩種

使用場景

  • 多種算法需要在運(yùn)行時(shí)切換:如支付方式、排序算法等,根據(jù)不同的條件選擇合適的算法。
  • 避免大量的條件語句:使用策略模式可以替代 if-elseswitch 語句,提高代碼的可讀性和可維護(hù)性。
  • 行為的變化需要獨(dú)立于上下文:算法的變化不應(yīng)影響使用它的代碼,遵循開閉原則。

下面以最常用的場景,支付場景作為演示Demo

假設(shè)我們有一個(gè)電商平臺,支持多種支付方式,如銀聯(lián)支付、支付寶和微信支付。
使用策略模式可以讓支付方式的選擇和實(shí)現(xiàn)獨(dú)立,方便擴(kuò)展新的支付方式。

1. 定義策略接口

// 支付策略接口
public interface PaymentStrategy {
    void pay(double amount);
}

2. 實(shí)現(xiàn)具體策略

// 銀聯(lián)支付策略
public class UnionPayPayment implements PaymentStrategy {
    private String cardNumber;
    private String cardHolderName;
    private String bankName;

    public UnionPayPayment(String cardNumber, String cardHolderName, String bankName) {
        this.cardNumber = cardNumber;
        this.cardHolderName = cardHolderName;
        this.bankName = bankName;
    }

    @Override
    public void pay(double amount) {
        // 銀聯(lián)支付的具體實(shí)現(xiàn)
        System.out.println("使用銀聯(lián)卡支付 " + amount + " 元。");
    }
}

// 支付寶支付策略
public class AlipayPayment implements PaymentStrategy {
    private String alipayId;

    public AlipayPayment(String alipayId) {
        this.alipayId = alipayId;
    }

    @Override
    public void pay(double amount) {
        // 支付寶支付的具體實(shí)現(xiàn)
        System.out.println("使用支付寶支付 " + amount + " 元。");
    }
}

// 微信支付策略
public class WeChatPayment implements PaymentStrategy {
    private String weChatId;

    public WeChatPayment(String weChatId) {
        this.weChatId = weChatId;
    }

    @Override
    public void pay(double amount) {
        // 微信支付的具體實(shí)現(xiàn)
        System.out.println("使用微信支付 " + amount + " 元。");
    }
}

3. 創(chuàng)建上下文類

// 購物車類,持有支付策略
public class ShoppingCart {
    private List<Item> items;
    private PaymentStrategy paymentStrategy;

    public ShoppingCart() {
        items = new ArrayList<>();
    }

    // 添加商品
    public void addItem(Item item) {
        items.add(item);
    }

    // 設(shè)置支付策略
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    // 結(jié)賬
    public void checkout() {
        double total = calculateTotal();
        paymentStrategy.pay(total);
    }

    // 計(jì)算總價(jià)
    private double calculateTotal() {
        double sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }
}

4. 商品類

public class Item {
    private String name;
    private double price;

    public Item(String name, double price) {
        this.name = name;
        this.price = price;
    }

    // 獲取價(jià)格
    public double getPrice() {
        return price;
    }
}

5. 客戶端代碼

public class Client {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // 添加商品
        cart.addItem(new Item("蘋果", 10.0));
        cart.addItem(new Item("香蕉", 20.0));

        // 選擇支付方式并支付

        // 使用銀聯(lián)支付
        PaymentStrategy unionPayPayment = new UnionPayPayment("6222020200000000", "張三", "中國銀行");
        cart.setPaymentStrategy(unionPayPayment);
        cart.checkout();

        // 使用支付寶支付
        PaymentStrategy alipayPayment = new AlipayPayment("alipay_user_id");
        cart.setPaymentStrategy(alipayPayment);
        cart.checkout();

        // 使用微信支付
        PaymentStrategy weChatPayment = new WeChatPayment("wechat_user_id");
        cart.setPaymentStrategy(weChatPayment);
        cart.checkout();
    }
}

輸出結(jié)果

使用銀聯(lián)卡支付 30.0 元。
使用支付寶支付 30.0 元。
使用微信支付 30.0 元。

6. 新增支付方式

如果需要新增一種支付方式,例如 Apple Pay,只需新增一個(gè)策略類:

// Apple Pay 支付策略
public class ApplePayPayment implements PaymentStrategy {
    private String appleId;

    public ApplePayPayment(String appleId) {
        this.appleId = appleId;
    }

    @Override
    public void pay(double amount) {
        // Apple Pay 支付的具體實(shí)現(xiàn)
        System.out.println("使用 Apple Pay 支付 " + amount + " 元。");
    }
}

在客戶端代碼中使用:

// 使用 Apple Pay 支付
PaymentStrategy applePayPayment = new ApplePayPayment("apple_user_id");
cart.setPaymentStrategy(applePayPayment);
cart.checkout();

策略模式與抽象工廠模式的區(qū)別

雖然策略模式和抽象工廠模式都涉及到對象的創(chuàng)建和使用,但它們的目的和應(yīng)用場景不同:

  • 策略模式:關(guān)注算法的封裝和互換,旨在運(yùn)行時(shí)靈活地選擇和切換算法。它將行為或算法抽象成獨(dú)立的策略類,使得不同的策略可以互相替換,客戶端通過上下文來使用策略。

  • 抽象工廠模式:關(guān)注產(chǎn)品族的創(chuàng)建,旨在提供一個(gè)接口,用于創(chuàng)建一系列相關(guān)或相互依賴的對象,而無需指定它們的具體類。抽象工廠模式主要用于創(chuàng)建對象,隱藏對象創(chuàng)建的細(xì)節(jié),確保客戶端使用的是同一產(chǎn)品族的對象。

區(qū)別總結(jié)

  • 目的不同:策略模式主要解決算法的選擇和切換問題,抽象工廠模式主要解決對象的創(chuàng)建問題。

  • 結(jié)構(gòu)不同:策略模式包含策略接口和具體策略類,客戶端通過上下文與策略交互;抽象工廠模式包含抽象工廠、具體工廠、抽象產(chǎn)品和具體產(chǎn)品,客戶端通過工廠創(chuàng)建產(chǎn)品。

  • 使用方式不同:策略模式強(qiáng)調(diào)算法的可替換性,通常在運(yùn)行時(shí)決定使用哪種策略;抽象工廠模式強(qiáng)調(diào)產(chǎn)品族的一致性,通常在編譯時(shí)就確定了具體的工廠和產(chǎn)品。

在支付場景中

  • 策略模式:我們使用策略模式來封裝不同的支付方式(策略),如銀聯(lián)支付、支付寶支付、微信支付等。每種支付方式實(shí)現(xiàn)了相同的策略接口,可以互相替換,客戶端可以在運(yùn)行時(shí)選擇不同的支付策略。

  • 抽象工廠模式:假設(shè)我們需要?jiǎng)?chuàng)建一系列相關(guān)的支付對象,例如支付方式、支付驗(yàn)證、支付日志等,且這些對象在不同的平臺(如國內(nèi)支付平臺、國際支付平臺)有不同的實(shí)現(xiàn)。這時(shí)可以使用抽象工廠模式,提供一個(gè)接口來創(chuàng)建相關(guān)的支付對象,確保同一平臺的對象一起工作。

策略模式的優(yōu)勢

  1. 符合開閉原則:添加新的策略無需修改原有代碼,擴(kuò)展性好。
  2. 避免使用多重條件判斷:策略模式通過多態(tài)替代了條件判斷,代碼更簡潔。
  3. 提高代碼的靈活性:可以在運(yùn)行時(shí)動(dòng)態(tài)更換策略,滿足不同的業(yè)務(wù)需求。
  4. 策略獨(dú)立,便于維護(hù):每個(gè)策略都封裝在獨(dú)立的類中,便于單獨(dú)修改和測試。
  5. 可復(fù)用性高:策略類可以在不同的上下文中復(fù)用,減少重復(fù)代碼。

策略模式的意義

  • 解耦算法和使用場景:將算法的實(shí)現(xiàn)和使用它的代碼分離,方便獨(dú)立地更改或擴(kuò)展算法。
  • 提高系統(tǒng)的可維護(hù)性:算法的更改不影響上下文,降低了修改的風(fēng)險(xiǎn)。
  • 滿足不同的需求:通過靈活地選擇策略,可以滿足不同客戶或環(huán)境的需求。

啟發(fā)

策略模式的應(yīng)用超越了代碼層面,它體現(xiàn)了一種面向變化、擁抱變化的思想。在快速發(fā)展的技術(shù)領(lǐng)域,我們需要構(gòu)建具有彈性和適應(yīng)性的系統(tǒng),以應(yīng)對未來的挑戰(zhàn)。通過合理地應(yīng)用策略模式,我們可以設(shè)計(jì)出更具生命力的軟件,為用戶提供更好的體驗(yàn)。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容