嗯,我的程式碼沒有else
系列,一個設計模式業務真實使用的golang系列。
前言
本系列主要分享,如何在我們的真實業務場景中使用設計模式。
本系列文章主要採用如下結構:
- 什麼是「XX設計模式」?
- 什麼真實業務場景可以使用「XX設計模式」?
- 怎麼用「XX設計模式」?
本文主要介紹「策略模式」如何在真實業務場景中使用。
什麼是「策略模式」?
「策略模式」比較簡單,大家平常工作中應該經常使用到,所以本文作為複習,幫助大家溫故知新。我們先來看下定義:
不同的演算法按照統一的標準封裝,客戶端根據不同的場景,決策使用何種演算法。
上面的概念的關鍵詞:
- 演算法:就是行為
- 標準:就是interface
- 客戶端:客戶端是相對的,誰呼叫誰就是客戶端
- 場景:判斷條件
- 決策:判斷的過程
概念很容易理解,不多說。
「策略模式」的優勢:
- 典型的高內聚:演算法和演算法之間完全獨立、互不干擾
- 典型的鬆耦合:客戶端依賴的是介面的抽象方法
- 沉澱:每一個封裝好的演算法都是這個技術團隊的財富,且未來可以被輕易的修改、複用
什麼真實業務場景可以用「策略模式」?
每一行程式碼下面的十字路口
當程式碼的下一步面臨選擇的時候都可以使用「策略模式」,我們把不同選擇的演算法按照統一的標準封裝,得到一類演算法集的過程,就是實現「策略模式」的過程。
我們有哪些真實業務場景可以用「策略模式」呢?
比如:
- 快取: 使用什麼樣的nosql
- 儲存: 使用什麼樣的DB
- 支付: 使用什麼樣的支付方式
- 等等...
本文以支付介面舉例,說明「策略模式」的具體使用。
怎麼用「策略模式」?
關於怎麼用,完全可以生搬硬套我總結的使用設計模式的四個步驟:
- 業務梳理
- 業務流程圖
- 程式碼建模
- 程式碼demo
業務梳理
我們以某團的訂單支付頁面為例,頁面上的每一個支付選項都是一個支付策略。如下:
使用者可以使用:
- 美團支付(策略)
- 微信支付(策略)
- 支付寶支付(策略)
使用者決定使用美團支付下的銀行卡支付方式的引數
使用者決定使用支付寶網頁版支付方式的引數
注:不一定完全準確。
業務流程圖
我們通過梳理的文字業務流程得到了如下的業務流程圖:
注:流程不一定完全準確。
程式碼建模
「策略模式」的核心是介面:
-
PaymentInterface
-
Pay(ctx *Context) error
當前支付方式的支付邏輯 -
Refund(ctx *Context) error
當前支付方式的退款邏輯
-
虛擬碼如下:
// 定義一個支付介面
- `PaymentInterface`
+ 抽象方法`Pay(ctx *Context) error`: 當前支付方式的支付邏輯
+ 抽象方法`Refund(ctx *Context) error`: 當前支付方式的退款邏輯
// 定義具體的支付方式 實現介面`PaymentInterface`
- 具體的微信支付方式`WechatPay`
+ 實現方法`Pay`: 支付邏輯
+ 實現方法`Refund`: 支付邏輯
- 具體的支付寶支付網頁版方式`AliPayWap`
+ 實現方法`Pay`: 支付邏輯
+ 實現方法`Refund`: 支付邏輯
- 具體的支付寶支付網頁版方式`BankPay`
+ 實現方法`Pay`: 支付邏輯
+ 實現方法`Refund`: 支付邏輯
// 客戶端程式碼
通過介面引數pay_type的值判斷是哪種支付方式策略
同時得到了我們的UML圖:
程式碼demo
package main
import (
"fmt"
"runtime"
)
//------------------------------------------------------------
//我的程式碼沒有`else`系列
//策略模式
//@auhtor TIGERB<https://github.com/TIGERB>
//------------------------------------------------------------
const (
// ConstWechatPay 微信支付
ConstWechatPay = "wechat_pay"
// ConstAliPayWap 支付寶支付 網頁版
ConstAliPayWap = "AliPayWapwap"
// ConstBankPay 銀行卡支付
ConstBankPay = "quickbank"
)
// Context 上下文
type Context struct {
// 使用者選擇的支付方式
PayType string `json:"pay_type"`
}
// PaymentInterface 支付方式介面
type PaymentInterface interface {
Pay(ctx *Context) error // 支付
Refund(ctx *Context) error // 退款
}
// WechatPay 微信支付
type WechatPay struct {
}
// Pay 當前支付方式的支付邏輯
func (p *WechatPay) Pay(ctx *Context) (err error) {
// 當前策略的業務邏輯寫這
fmt.Println(runFuncName(), "使用微信支付...")
return
}
// Refund 當前支付方式的支付邏輯
func (p *WechatPay) Refund(ctx *Context) (err error) {
// 當前策略的業務邏輯寫這
fmt.Println(runFuncName(), "使用微信退款...")
return
}
// AliPayWap 支付寶網頁版
type AliPayWap struct {
}
// Pay 當前支付方式的支付邏輯
func (p *AliPayWap) Pay(ctx *Context) (err error) {
// 當前策略的業務邏輯寫這
fmt.Println(runFuncName(), "使用支付寶網頁版支付...")
return
}
// Refund 當前支付方式的支付邏輯
func (p *AliPayWap) Refund(ctx *Context) (err error) {
// 當前策略的業務邏輯寫這
fmt.Println(runFuncName(), "使用支付寶網頁版退款...")
return
}
// BankPay 銀行卡支付
type BankPay struct {
}
// Pay 當前支付方式的支付邏輯
func (p *BankPay) Pay(ctx *Context) (err error) {
// 當前策略的業務邏輯寫這
fmt.Println(runFuncName(), "使用銀行卡支付...")
return
}
// Refund 當前支付方式的支付邏輯
func (p *BankPay) Refund(ctx *Context) (err error) {
// 當前策略的業務邏輯寫這
fmt.Println(runFuncName(), "使用銀行卡退款...")
return
}
// 獲取正在執行的函式名
func runFuncName() string {
pc := make([]uintptr, 1)
runtime.Callers(2, pc)
f := runtime.FuncForPC(pc[0])
return f.Name()
}
func main() {
// 相對於被呼叫的支付策略 這裡就是支付策略的客戶端
// 業務上下文
ctx := &Context{
PayType: "wechat_pay",
}
// 獲取支付方式
var instance PaymentInterface
switch ctx.PayType {
case ConstWechatPay:
instance = &WechatPay{}
case ConstAliPayWap:
instance = &AliPayWap{}
case ConstBankPay:
instance = &BankPay{}
default:
panic("無效的支付方式")
}
// 支付
instance.Pay(ctx)
}
程式碼執行結果:
[Running] go run "../easy-tips/go/src/patterns/strategy/strategy.go"
main.(*WechatPay).Pay 使用微信支付...
結語
最後總結下,「策略模式」抽象過程的核心是:
在每一行程式碼下面的十字路口
- 宣告標準:定義
interface
- 封裝演算法:按照標準
interface
封裝分支程式碼,得到每一個具體策略 - 構建演算法集:每一個具體策略構成策略池子 -> 這就是沉澱的過程
特別說明:
1. 我的程式碼沒有`else`,只是一個在程式碼合理設計的情況下自然而然無限接近或者達到的結果,並不是一個硬性的目標,務必較真。
2. 本系列的一些設計模式的概念可能和原概念存在差異,因為會結合實際使用,取其精華,適當改變,靈活使用。
文章列表
- 程式碼模板 | 我的程式碼沒有else
- 鏈式呼叫 | 我的程式碼沒有else
- 程式碼元件 | 我的程式碼沒有else
- 訂閱通知 | 我的程式碼沒有else
- 客戶決策 | 我的程式碼沒有else
- 狀態變換 | 我的程式碼沒有else
我的程式碼沒有else系列 更多文章 點選此處檢視