天天看點

【設計模式】通過購買網易雲音樂會員了解政策模式

我們在購買網易雲音樂會員的時候,支付頁有兩個選項,分别是支付寶支付和微信支付,這個背景結算進行開發的時候,如果要提高程式的可維護性,那麼大機率會使用标題所提到的政策模式。

【設計模式】通過購買網易雲音樂會員了解政策模式

1.模式定義

這裡就直接給出GoF的官方定義吧,後續我們圍繞這個定義展開:

Define a family of algorithms, encapsulate each one, and make them interchangeable.

定義一個算法家族,分别封裝起來,使得它們之間可以互相變換。

可以看到,這個定義就非常吻合我們的這個支付場景了,網易雲可以内容實作了支付寶支付政策,微信支付政策,使用者根據情況選擇其中的一種支付方式,便是選擇其中的一種支付政策。

2.模式結構

這裡是正常的政策模式的模式結構圖

【設計模式】通過購買網易雲音樂會員了解政策模式

https://refactoringguru.cn/design-patterns/strategy

實際開發我們會考慮各種優化,以及使用各種開發架構如Spring,會和結構圖中有所差別:

  • 設定政策的方式通常是傳入政策枚舉,而不是new一個政策并傳入context中,基于Spring進行開發的企業級項目,通常不會有手動new一個服務的操作,而是注冊為一個個的Spring的bean。
  • 每個政策都應該單例化,而Spring預設就是單例模式,完美結合。

3.模式實作

本着了解的模式為先的原則,是以代碼結構力求簡單明了。同時貼近實際生産,如使用了Spring架構等。

【設計模式】通過購買網易雲音樂會員了解政策模式

3.1.定義支付枚舉

後續新增支付方式,記得這裡維護下

package com.example.designpattern.strategy.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 支付政策
 *
 * @author hongcunlin
 */
@Getter
@AllArgsConstructor
public enum PayEnums {
    /**
     * 支付寶支付
     */
    ALI(1, "支付寶支付"),

    /**
     * 微信支付
     */
    WE_CHAT(2, "微信支付");

    /**
     * 支付類型碼
     */
    private final Integer code;

    /**
     * 支付類型名稱
     */
    private final String name;
}           

3.2.定義支付政策

package com.example.designpattern.strategy.paystrategy;

/**
 * 支付政策
 *
 * @author hongcunlin
 */
public interface PayStrategy {
    /**
     * 支付
     */
    void pay();
}           

3.3.實作支付寶支付、微信支付政策

package com.example.designpattern.strategy.paystrategy.impl;

import com.example.designpattern.strategy.paystrategy.PayStrategy;
import org.springframework.stereotype.Service;

/**
 * 支付寶支付
 *
 * @author hongcunlin
 */
@Service
public class AliPayStrategy implements PayStrategy {
    @Override
    public void pay() {
        System.out.println("支付寶支付");
    }
}           
package com.example.designpattern.strategy.paystrategy.impl;

import com.example.designpattern.strategy.paystrategy.PayStrategy;
import org.springframework.stereotype.Service;

/**
 * 微信支付
 *
 * @author hongcunlin
 */
@Service
public class WeChatPayStrategy implements PayStrategy {
    @Override
    public void pay() {
        System.out.println("微信支付");
    }
}           

另外實際開發中,通常會有一個預設的實作用于兜底。

package com.example.designpattern.strategy.paystrategy.impl;

import com.example.designpattern.strategy.paystrategy.PayStrategy;
import org.springframework.stereotype.Service;

/**
 * 預設支付
 *
 * @author hongcunlin
 */
@Service
public class DefaultPayStrategy implements PayStrategy {
    @Override
    public void pay() {
        System.out.println("支付失敗");
    }
}           

3.4.定義支付上下文

這類注入了各大政策實作

package com.example.designpattern.strategy.context;

import com.example.designpattern.strategy.enums.PayEnums;
import com.example.designpattern.strategy.paystrategy.PayStrategy;
import com.example.designpattern.strategy.paystrategy.impl.AliPayStrategy;
import com.example.designpattern.strategy.paystrategy.impl.DefaultPayStrategy;
import com.example.designpattern.strategy.paystrategy.impl.WeChatPayStrategy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 支付上下文
 *
 * @author hongcunlin
 */
@Component
public class PayContext {
    /**
     * 支付政策
     */
    private PayStrategy payStrategy;

    /**
     * 預設支付
     */
    @Resource
    private DefaultPayStrategy defaultPayStrategy;

    /**
     * 支付寶支付
     */
    @Resource
    private AliPayStrategy aliPayStrategy;

    /**
     * 微信支付
     */
    @Resource
    private WeChatPayStrategy weChatPayStrategy;

    /**
     * 設定支付政策
     *
     * @param code 支付政策碼
     */
    public void setPayStrategy(Integer code) {
        if (PayEnums.ALI.getCode().equals(code)) {
            payStrategy = aliPayStrategy;
        } else if (PayEnums.WE_CHAT.getCode().equals(code)) {
            payStrategy = weChatPayStrategy;
        } else {
            payStrategy = defaultPayStrategy;
        }
    }

    /**
     * 執行政策
     */
    public void execute() {
        if (null == payStrategy) {
            throw new RuntimeException("未設定支付方式");
        }
        payStrategy.pay();
    }
}           

3.5.測試

我們注入支付上下文PayContext,通過枚舉指定支付的方式,執行PayContext的execute方法完成支付。

package com.example.designpattern.strategy;

import com.example.designpattern.strategy.context.PayContext;
import com.example.designpattern.strategy.enums.PayEnums;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
public class DesignPatternTest {
    /**
     * 支付上下文
     */
    @Resource
    private PayContext payContext;

    /**
     * 測試
     */
    @Test
    public void test() {
        payContext.setPayStrategy(PayEnums.ALI.getCode());
        payContext.execute();
    }
}
           

這是運作結果,可以看到,是符合我們的預期的

【設計模式】通過購買網易雲音樂會員了解政策模式

4.模式優點

最後來簡單聊聊使用了政策模式後的有點,主要展現在如果後面網易雲音樂會員支援方式,新增了京東支付、抖音支付等支付方式,我們可以通過新增相應的京東支付政策、抖音支付政策即可,原有的主流程無需改變,即:

每新增一個政策,隻需要新增政策的實作即可,無需改動原有的流程。

後續有空,我們接着依據現實生活,聊聊設計模式中的其他設計模式。