代碼模板|我的代碼沒有else

NO IMAGE

嗯,我的代碼沒有else系列,一個設計模式業務真實使用的golang系列。

代碼模板|我的代碼沒有else

前言

本系列主要分享,如何在我們的真實業務場景中使用設計模式。

本系列文章主要採用如下結構:

  • 什麼是「XX設計模式」?
  • 什麼真實業務場景可以使用「XX設計模式」?
  • 怎麼用「XX設計模式」?

本文主要介紹「模板模式」如何在真實業務場景中使用。

什麼是「模板模式」?

抽象類裡定義好算法的執行步驟具體算法,以及可能發生變化的算法定義為抽象方法。不同的子類繼承該抽象類,並實現父類的抽象方法。

模板模式的優勢:

  • 不變的算法被繼承複用:不變的部分高度封裝、複用。
  • 變化的算法子類繼承並具體實現:變化的部分子類只需要具體實現抽象的部分即可,方便擴展,且可無限擴展。

什麼真實業務場景可以用「模板模式」?

滿足如下要求的所有場景:

算法執行的步驟是穩定不變的,但是具體的某些算法可能存在化的場景。

怎麼理解,舉個例子:比如說你煮個面,必然需要先燒水,水燒開之後再放面進去,以上的流程我們稱之為煮麵過程。可知:這個煮麵過程的步驟是穩定不變的,但是在不同的環境燒水的方式可能不盡相同,也許有的人用天然氣燒水、有的人用電磁爐燒水、有的人用柴火燒水,等等。我們可以得到以下結論:

  • 煮麵過程的步驟是穩定不變的
  • 煮麵過程的燒水方式是可變的

我們有哪些真實業務場景可以用「模板模式」呢?

比如抽獎系統的抽獎接口,為什麼:

  • 抽獎的步驟是穩定不變的 -> 不變的算法執行步驟
  • 不同抽獎類型活動在某些邏輯處理方式可能不同 -> 變的某些算法

怎麼用「模板模式」?

關於怎麼用,完全可以生搬硬套我總結的使用設計模式的四個步驟:

  • 業務梳理
  • 業務流程圖
  • 代碼建模
  • 代碼demo

業務梳理

我通過歷史上接觸過的各種抽獎場景(紅包雨、糖果雨、打地鼠、大轉盤(九宮格)、考眼力、答題闖關、遊戲闖關、支付刮刮樂、積分刮刮樂等等),按照真實業務需求梳理了以下抽獎業務抽獎接口的大致文本流程。

瞭解具體業務請點擊《通用抽獎工具之需求分析 | SkrShop》

主步驟主邏輯抽獎類型子步驟子邏輯
1校驗活動編號(serial_no)是否存在、並獲取活動信息
2校驗活動、場次是否正在進行
3其他參數校驗(不同活動類型實現不同)
4活動抽獎次數校驗(同時扣減)
5活動是否需要消費積分
6場次抽獎次數校驗(同時扣減)
7獲取場次獎品信息
8獲取node獎品信息(不同活動類型實現不同)按時間抽獎類型1do nothing(抽取該場次的獎品即可,無需其他邏輯)
8按抽獎次數抽獎類型1判斷是該用戶第幾次抽獎
82獲取對應node的獎品信息
83複寫原所有獎品信息(抽取該node節點的獎品)
8按數額範圍區間抽獎1判斷屬於哪個數額區間
82獲取對應node的獎品信息
83複寫原所有獎品信息(抽取該node節點的獎品)
9抽獎
10獎品數量判斷
11組裝獎品信息

注:流程不一定完全準確

結論:

  • 主邏輯是穩定不變的
  • 其他參數校驗獲取node獎品信息的算法是可變的

業務流程圖

我們通過梳理的文本業務流程得到了如下的業務流程圖:

代碼模板|我的代碼沒有else

代碼建模

通過上面的分析我們可以得到:

一個抽象類
- 具體共有方法`Run`,裡面定義了算法的執行步驟
- 具體私有方法,不會發生變化的具體方法
- 抽象方法,會發生變化的方法
子類一(按時間抽獎類型)
- 繼承抽象類父類
- 實現抽象方法
子類二(按抽獎次數抽獎類型)
- 繼承抽象類父類
- 實現抽象方法
子類三(按數額範圍區間抽獎)
- 繼承抽象類父類
- 實現抽象方法

但是golang裡面沒有繼承的概念,我們就把對抽象類裡抽象方法的依賴轉化成對接口interface裡抽象方法的依賴,同時也可以利用合成複用的方式“繼承”模板:

抽象行為的接口`BehaviorInterface`(包含如下需要實現的方法)
- 其他參數校驗的方法`checkParams`
- 獲取node獎品信息的方法`getPrizesByNode`
抽獎結構體類
- 具體共有方法`Run`,裡面定義了算法的執行步驟
- 具體私有方法`checkParams` 裡面的邏輯實際依賴的接口BehaviorInterface.checkParams(ctx)的抽象方法
- 具體私有方法`getPrizesByNode` 裡面的邏輯實際依賴的接口BehaviorInterface.getPrizesByNode(ctx)的抽象方法
- 其他具體私有方法,不會發生變化的具體方法
實現`BehaviorInterface`的結構體一(按時間抽獎類型)
- 實現接口方法
實現`BehaviorInterface`的結構體二(按抽獎次數抽獎類型)
- 實現接口方法
實現`BehaviorInterface`的結構體三(按數額範圍區間抽獎)
- 實現接口方法

同時得到了我們的UML圖:

代碼模板|我的代碼沒有else

代碼demo

package main
import (
"fmt"
"runtime"
)
//------------------------------------------------------------
//我的代碼沒有`else`系列
//模板模式
//@auhtor TIGERB<https://github.com/TIGERB>
//------------------------------------------------------------
const (
// ConstActTypeTime 按時間抽獎類型
ConstActTypeTime int32 = 1
// ConstActTypeTimes 按抽獎次數抽獎
ConstActTypeTimes int32 = 2
// ConstActTypeAmount 按數額範圍區間抽獎
ConstActTypeAmount int32 = 3
)
// Context 上下文
type Context struct {
ActInfo *ActInfo
}
// ActInfo 上下文
type ActInfo struct {
// 活動抽獎類型1: 按時間抽獎 2: 按抽獎次數抽獎 3:按數額範圍區間抽獎
ActivityType int32
// 其他字段略
}
// BehaviorInterface 不同抽獎類型的行為差異的抽象接口
type BehaviorInterface interface {
// 其他參數校驗(不同活動類型實現不同)
checkParams(ctx *Context) error
// 獲取node獎品信息(不同活動類型實現不同)
getPrizesByNode(ctx *Context) error
}
// TimeDraw 具體抽獎行為
// 按時間抽獎類型 比如紅包雨
type TimeDraw struct{}
// checkParams 其他參數校驗(不同活動類型實現不同)
func (draw TimeDraw) checkParams(ctx *Context) (err error) {
fmt.Println(runFuncName(), "按時間抽獎類型:特殊參數校驗...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
func (draw TimeDraw) getPrizesByNode(ctx *Context) (err error) {
fmt.Println(runFuncName(), "do nothing(抽取該場次的獎品即可,無需其他邏輯)...")
return
}
// TimesDraw 具體抽獎行為
// 按抽獎次數抽獎類型 比如答題闖關
type TimesDraw struct{}
// checkParams 其他參數校驗(不同活動類型實現不同)
func (draw TimesDraw) checkParams(ctx *Context) (err error) {
fmt.Println(runFuncName(), "按抽獎次數抽獎類型:特殊參數校驗...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
func (draw TimesDraw) getPrizesByNode(ctx *Context) (err error) {
fmt.Println(runFuncName(), "1. 判斷是該用戶第幾次抽獎...")
fmt.Println(runFuncName(), "2. 獲取對應node的獎品信息...")
fmt.Println(runFuncName(), "3. 複寫原所有獎品信息(抽取該node節點的獎品)...")
return
}
// AmountDraw 具體抽獎行為
// 按數額範圍區間抽獎 比如訂單金額刮獎
type AmountDraw struct{}
// checkParams 其他參數校驗(不同活動類型實現不同)
func (draw *AmountDraw) checkParams(ctx *Context) (err error) {
fmt.Println(runFuncName(), "按數額範圍區間抽獎:特殊參數校驗...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
func (draw *AmountDraw) getPrizesByNode(ctx *Context) (err error) {
fmt.Println(runFuncName(), "1. 判斷屬於哪個數額區間...")
fmt.Println(runFuncName(), "2. 獲取對應node的獎品信息...")
fmt.Println(runFuncName(), "3. 複寫原所有獎品信息(抽取該node節點的獎品)...")
return
}
// Lottery 抽獎模板
type Lottery struct {
// 不同抽獎類型的抽象行為
concreteBehavior BehaviorInterface
}
// Run 抽獎算法
// 穩定不變的算法步驟
func (lottery *Lottery) Run(ctx *Context) (err error) {
// 具體方法:校驗活動編號(serial_no)是否存在、並獲取活動信息
if err = lottery.checkSerialNo(ctx); err != nil {
return err
}
// 具體方法:校驗活動、場次是否正在進行
if err = lottery.checkStatus(ctx); err != nil {
return err
}
// ”抽象方法“:其他參數校驗
if err = lottery.checkParams(ctx); err != nil {
return err
}
// 具體方法:活動抽獎次數校驗(同時扣減)
if err = lottery.checkTimesByAct(ctx); err != nil {
return err
}
// 具體方法:活動是否需要消費積分
if err = lottery.consumePointsByAct(ctx); err != nil {
return err
}
// 具體方法:場次抽獎次數校驗(同時扣減)
if err = lottery.checkTimesBySession(ctx); err != nil {
return err
}
// 具體方法:獲取場次獎品信息
if err = lottery.getPrizesBySession(ctx); err != nil {
return err
}
// ”抽象方法“:獲取node獎品信息
if err = lottery.getPrizesByNode(ctx); err != nil {
return err
}
// 具體方法:抽獎
if err = lottery.drawPrizes(ctx); err != nil {
return err
}
// 具體方法:獎品數量判斷
if err = lottery.checkPrizesStock(ctx); err != nil {
return err
}
// 具體方法:組裝獎品信息
if err = lottery.packagePrizeInfo(ctx); err != nil {
return err
}
return
}
// checkSerialNo 校驗活動編號(serial_no)是否存在
func (lottery *Lottery) checkSerialNo(ctx *Context) (err error) {
fmt.Println(runFuncName(), "校驗活動編號(serial_no)是否存在、並獲取活動信息...")
// 獲取活動信息偽代碼
ctx.ActInfo = &ActInfo{
// 假設當前的活動類型為按抽獎次數抽獎
ActivityType: ConstActTypeTimes,
}
// 獲取當前抽獎類型的具體行為
switch ctx.ActInfo.ActivityType {
case 1:
// 按時間抽獎
lottery.concreteBehavior = &TimeDraw{}
case 2:
// 按抽獎次數抽獎
lottery.concreteBehavior = &TimesDraw{}
case 3:
// 按數額範圍區間抽獎
lottery.concreteBehavior = &AmountDraw{}
default:
return fmt.Errorf("不存在的活動類型")
}
return
}
// checkStatus 校驗活動、場次是否正在進行
func (lottery *Lottery) checkStatus(ctx *Context) (err error) {
fmt.Println(runFuncName(), "校驗活動、場次是否正在進行...")
return
}
// checkParams 其他參數校驗(不同活動類型實現不同)
// 不同場景變化的算法 轉化為依賴抽象
func (lottery *Lottery) checkParams(ctx *Context) (err error) {
// 實際依賴的接口的抽象方法
return lottery.concreteBehavior.checkParams(ctx)
}
// checkTimesByAct 活動抽獎次數校驗
func (lottery *Lottery) checkTimesByAct(ctx *Context) (err error) {
fmt.Println(runFuncName(), "活動抽獎次數校驗...")
return
}
// consumePointsByAct 活動是否需要消費積分
func (lottery *Lottery) consumePointsByAct(ctx *Context) (err error) {
fmt.Println(runFuncName(), "活動是否需要消費積分...")
return
}
// checkTimesBySession 活動抽獎次數校驗
func (lottery *Lottery) checkTimesBySession(ctx *Context) (err error) {
fmt.Println(runFuncName(), "活動抽獎次數校驗...")
return
}
// getPrizesBySession 獲取場次獎品信息
func (lottery *Lottery) getPrizesBySession(ctx *Context) (err error) {
fmt.Println(runFuncName(), "獲取場次獎品信息...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
// 不同場景變化的算法 轉化為依賴抽象
func (lottery *Lottery) getPrizesByNode(ctx *Context) (err error) {
// 實際依賴的接口的抽象方法
return lottery.concreteBehavior.getPrizesByNode(ctx)
}
// drawPrizes 抽獎
func (lottery *Lottery) drawPrizes(ctx *Context) (err error) {
fmt.Println(runFuncName(), "抽獎...")
return
}
// checkPrizesStock 獎品數量判斷
func (lottery *Lottery) checkPrizesStock(ctx *Context) (err error) {
fmt.Println(runFuncName(), "獎品數量判斷...")
return
}
// packagePrizeInfo 組裝獎品信息
func (lottery *Lottery) packagePrizeInfo(ctx *Context) (err error) {
fmt.Println(runFuncName(), "組裝獎品信息...")
return
}
func main() {
(&Lottery{}).Run(&Context{})
}
// 獲取正在運行的函數名
func runFuncName() string {
pc := make([]uintptr, 1)
runtime.Callers(2, pc)
f := runtime.FuncForPC(pc[0])
return f.Name()
}

以下是代碼執行結果:

[Running] go run ".../easy-tips/go/src/patterns/template/template.go"
main.(*Lottery).checkSerialNo 校驗活動編號(serial_no)是否存在、並獲取活動信息...
main.(*Lottery).checkStatus 校驗活動、場次是否正在進行...
main.TimesDraw.checkParams 按抽獎次數抽獎類型:特殊參數校驗...
main.(*Lottery).checkTimesByAct 活動抽獎次數校驗...
main.(*Lottery).consumePointsByAct 活動是否需要消費積分...
main.(*Lottery).checkTimesBySession 活動抽獎次數校驗...
main.(*Lottery).getPrizesBySession 獲取場次獎品信息...
main.TimesDraw.getPrizesByNode 1. 判斷是該用戶第幾次抽獎...
main.TimesDraw.getPrizesByNode 2. 獲取對應node的獎品信息...
main.TimesDraw.getPrizesByNode 3. 複寫原所有獎品信息(抽取該node節點的獎品)...
main.(*Lottery).drawPrizes 抽獎...
main.(*Lottery).checkPrizesStock 獎品數量判斷...
main.(*Lottery).packagePrizeInfo 組裝獎品信息...

demo代碼地址:github.com/TIGERB/easy…

代碼demo2(利用golang的合成複用特性實現)

package main
import (
"fmt"
"runtime"
)
//------------------------------------------------------------
//我的代碼沒有`else`系列
//模板模式
//@auhtor TIGERB<https://github.com/TIGERB>
//------------------------------------------------------------
const (
// ConstActTypeTime 按時間抽獎類型
ConstActTypeTime int32 = 1
// ConstActTypeTimes 按抽獎次數抽獎
ConstActTypeTimes int32 = 2
// ConstActTypeAmount 按數額範圍區間抽獎
ConstActTypeAmount int32 = 3
)
// Context 上下文
type Context struct {
ActInfo *ActInfo
}
// ActInfo 上下文
type ActInfo struct {
// 活動抽獎類型1: 按時間抽獎 2: 按抽獎次數抽獎 3:按數額範圍區間抽獎
ActivityType int32
// 其他字段略
}
// BehaviorInterface 不同抽獎類型的行為差異的抽象接口
type BehaviorInterface interface {
// 其他參數校驗(不同活動類型實現不同)
checkParams(ctx *Context) error
// 獲取node獎品信息(不同活動類型實現不同)
getPrizesByNode(ctx *Context) error
}
// TimeDraw 具體抽獎行為
// 按時間抽獎類型 比如紅包雨
type TimeDraw struct {
// 合成複用模板
Lottery
}
// checkParams 其他參數校驗(不同活動類型實現不同)
func (draw TimeDraw) checkParams(ctx *Context) (err error) {
fmt.Println(runFuncName(), "按時間抽獎類型:特殊參數校驗...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
func (draw TimeDraw) getPrizesByNode(ctx *Context) (err error) {
fmt.Println(runFuncName(), "do nothing(抽取該場次的獎品即可,無需其他邏輯)...")
return
}
// TimesDraw 具體抽獎行為
// 按抽獎次數抽獎類型 比如答題闖關
type TimesDraw struct {
// 合成複用模板
Lottery
}
// checkParams 其他參數校驗(不同活動類型實現不同)
func (draw TimesDraw) checkParams(ctx *Context) (err error) {
fmt.Println(runFuncName(), "按抽獎次數抽獎類型:特殊參數校驗...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
func (draw TimesDraw) getPrizesByNode(ctx *Context) (err error) {
fmt.Println(runFuncName(), "1. 判斷是該用戶第幾次抽獎...")
fmt.Println(runFuncName(), "2. 獲取對應node的獎品信息...")
fmt.Println(runFuncName(), "3. 複寫原所有獎品信息(抽取該node節點的獎品)...")
return
}
// AmountDraw 具體抽獎行為
// 按數額範圍區間抽獎 比如訂單金額刮獎
type AmountDraw struct {
// 合成複用模板
Lottery
}
// checkParams 其他參數校驗(不同活動類型實現不同)
func (draw *AmountDraw) checkParams(ctx *Context) (err error) {
fmt.Println(runFuncName(), "按數額範圍區間抽獎:特殊參數校驗...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
func (draw *AmountDraw) getPrizesByNode(ctx *Context) (err error) {
fmt.Println(runFuncName(), "1. 判斷屬於哪個數額區間...")
fmt.Println(runFuncName(), "2. 獲取對應node的獎品信息...")
fmt.Println(runFuncName(), "3. 複寫原所有獎品信息(抽取該node節點的獎品)...")
return
}
// Lottery 抽獎模板
type Lottery struct {
// 不同抽獎類型的抽象行為
ConcreteBehavior BehaviorInterface
}
// Run 抽獎算法
// 穩定不變的算法步驟
func (lottery *Lottery) Run(ctx *Context) (err error) {
// 具體方法:校驗活動編號(serial_no)是否存在、並獲取活動信息
if err = lottery.checkSerialNo(ctx); err != nil {
return err
}
// 具體方法:校驗活動、場次是否正在進行
if err = lottery.checkStatus(ctx); err != nil {
return err
}
// ”抽象方法“:其他參數校驗
if err = lottery.checkParams(ctx); err != nil {
return err
}
// 具體方法:活動抽獎次數校驗(同時扣減)
if err = lottery.checkTimesByAct(ctx); err != nil {
return err
}
// 具體方法:活動是否需要消費積分
if err = lottery.consumePointsByAct(ctx); err != nil {
return err
}
// 具體方法:場次抽獎次數校驗(同時扣減)
if err = lottery.checkTimesBySession(ctx); err != nil {
return err
}
// 具體方法:獲取場次獎品信息
if err = lottery.getPrizesBySession(ctx); err != nil {
return err
}
// ”抽象方法“:獲取node獎品信息
if err = lottery.getPrizesByNode(ctx); err != nil {
return err
}
// 具體方法:抽獎
if err = lottery.drawPrizes(ctx); err != nil {
return err
}
// 具體方法:獎品數量判斷
if err = lottery.checkPrizesStock(ctx); err != nil {
return err
}
// 具體方法:組裝獎品信息
if err = lottery.packagePrizeInfo(ctx); err != nil {
return err
}
return
}
// checkSerialNo 校驗活動編號(serial_no)是否存在
func (lottery *Lottery) checkSerialNo(ctx *Context) (err error) {
fmt.Println(runFuncName(), "校驗活動編號(serial_no)是否存在、並獲取活動信息...")
return
}
// checkStatus 校驗活動、場次是否正在進行
func (lottery *Lottery) checkStatus(ctx *Context) (err error) {
fmt.Println(runFuncName(), "校驗活動、場次是否正在進行...")
return
}
// checkParams 其他參數校驗(不同活動類型實現不同)
// 不同場景變化的算法 轉化為依賴抽象
func (lottery *Lottery) checkParams(ctx *Context) (err error) {
// 實際依賴的接口的抽象方法
return lottery.ConcreteBehavior.checkParams(ctx)
}
// checkTimesByAct 活動抽獎次數校驗
func (lottery *Lottery) checkTimesByAct(ctx *Context) (err error) {
fmt.Println(runFuncName(), "活動抽獎次數校驗...")
return
}
// consumePointsByAct 活動是否需要消費積分
func (lottery *Lottery) consumePointsByAct(ctx *Context) (err error) {
fmt.Println(runFuncName(), "活動是否需要消費積分...")
return
}
// checkTimesBySession 活動抽獎次數校驗
func (lottery *Lottery) checkTimesBySession(ctx *Context) (err error) {
fmt.Println(runFuncName(), "活動抽獎次數校驗...")
return
}
// getPrizesBySession 獲取場次獎品信息
func (lottery *Lottery) getPrizesBySession(ctx *Context) (err error) {
fmt.Println(runFuncName(), "獲取場次獎品信息...")
return
}
// getPrizesByNode 獲取node獎品信息(不同活動類型實現不同)
// 不同場景變化的算法 轉化為依賴抽象
func (lottery *Lottery) getPrizesByNode(ctx *Context) (err error) {
// 實際依賴的接口的抽象方法
return lottery.ConcreteBehavior.getPrizesByNode(ctx)
}
// drawPrizes 抽獎
func (lottery *Lottery) drawPrizes(ctx *Context) (err error) {
fmt.Println(runFuncName(), "抽獎...")
return
}
// checkPrizesStock 獎品數量判斷
func (lottery *Lottery) checkPrizesStock(ctx *Context) (err error) {
fmt.Println(runFuncName(), "獎品數量判斷...")
return
}
// packagePrizeInfo 組裝獎品信息
func (lottery *Lottery) packagePrizeInfo(ctx *Context) (err error) {
fmt.Println(runFuncName(), "組裝獎品信息...")
return
}
func main() {
ctx := &Context{
ActInfo: &ActInfo{
ActivityType: ConstActTypeAmount,
},
}
switch ctx.ActInfo.ActivityType {
case ConstActTypeTime: // 按時間抽獎類型
instance := &TimeDraw{}
instance.ConcreteBehavior = instance
instance.Run(ctx)
case ConstActTypeTimes: // 按抽獎次數抽獎
instance := &TimesDraw{}
instance.ConcreteBehavior = instance
instance.Run(ctx)
case ConstActTypeAmount: // 按數額範圍區間抽獎
instance := &AmountDraw{}
instance.ConcreteBehavior = instance
instance.Run(ctx)
default:
// 報錯
return
}
}
// 獲取正在運行的函數名
func runFuncName() string {
pc := make([]uintptr, 1)
runtime.Callers(2, pc)
f := runtime.FuncForPC(pc[0])
return f.Name()
}

以下是代碼執行結果:

[Running] go run ".../easy-tips/go/src/patterns/template/templateOther.go"
main.(*Lottery).checkSerialNo 校驗活動編號(serial_no)是否存在、並獲取活動信息...
main.(*Lottery).checkStatus 校驗活動、場次是否正在進行...
main.(*AmountDraw).checkParams 按數額範圍區間抽獎:特殊參數校驗...
main.(*Lottery).checkTimesByAct 活動抽獎次數校驗...
main.(*Lottery).consumePointsByAct 活動是否需要消費積分...
main.(*Lottery).checkTimesBySession 活動抽獎次數校驗...
main.(*Lottery).getPrizesBySession 獲取場次獎品信息...
main.(*AmountDraw).getPrizesByNode 1. 判斷屬於哪個數額區間...
main.(*AmountDraw).getPrizesByNode 2. 獲取對應node的獎品信息...
main.(*AmountDraw).getPrizesByNode 3. 複寫原所有獎品信息(抽取該node節點的獎品)...
main.(*Lottery).drawPrizes 抽獎...
main.(*Lottery).checkPrizesStock 獎品數量判斷...
main.(*Lottery).packagePrizeInfo 組裝獎品信息...

demo2代碼地址:github.com/TIGERB/easy…

結語

最後總結下,「模板模式」抽象過程的核心是把握不變

  • 不變:Run方法裡的抽獎步驟 -> 被繼承複用
  • 變:不同場景下 -> 被具體實現
    • checkParams參數校驗邏輯
    • getPrizesByNode獲取該節點獎品的邏輯
特別說明:
1. 我的代碼沒有`else`,只是一個在代碼合理設計的情況下自然而然無限接近或者達到的結果,並不是一個硬性的目標,務必較真。
2. 本系列的一些設計模式的概念可能和原概念存在差異,因為會結合實際使用,取其精華,適當改變,靈活使用。

我的代碼沒有else系列 更多文章 點擊此處查看

代碼模板|我的代碼沒有else

相關文章

前後端分離中,使用JSON格式登錄原來這麼簡單!

DelayedMessage插件實現RabbitMQ延遲隊列

SameSite小識

徹底弄清元素的offsetHeight、scrollHeight、clientHeight…