設計模式 in Go: Strategy

行爲模式旨在解決對象間通信和交互相關的問題,專注於定義那些複雜到無法靜態設計的協作策略,這些協作策略使得程序可以在運行時動態地進行職責派遣以實現更好的擴展。

今天我們開始第 9 個行爲模式的學習 —— Strategy(策略模式)。

問題背景:

策略模式用於當有多種可以互換的算法或策略來完成特定任務時。使用這些算法的客戶端不需要知道算法的具體實現細節。

解決方案:

定義一個所有策略都將使用的接口。創建多個實現此公共接口的策略類。上下文對象將接收一個策略對象,並將工作委託給該對象,而不是自行完成工作。

Sports Rank List / Score Board

以下是一個示例,假設我們希望擁有不同的排名列表,並且這些列表的最大元素數量各不相同。我們知道不同長度的列表應該採用不同的排序算法以滿足性能要求。好的,讓我們使用策略模式來解決這個排名列表問題。

Strategy Pattern

示例代碼:

package strategy

import (
"sort"
)

// The Sorter interface declares a method for sorting
type Sorterinterface {
 Sort(values []RankItem)
}

// InsertSort implements insert sort
type InsertSortstruct{}

func(b *InsertSort) Sort(values []RankItem) {
// Insert sort implementation , well, if len(values) < 12,
// sort.Slice(values, lessfunc) indeed uses insert sort.
 sort.Slice(values,func(i, jint)bool {
return values[i].Score > values[j].Score
 })
}

// QuickSort implements quicksort
type QuickSortstruct{}

func(q *QuickSort) Sort(values []RankItem) {
// Quicksort implementation
 sort.Slice(values,func(i, jint)bool {
return values[i].Score > values[j].Score
 })
}

// NewRankList creates a new rank list
funcNewRankList(capint, sorter Sorter) *RankList {
return &RankList{
 capacity:cap,
 sorter: sorter,
 }
}

// RankItem is a ranked item in the rank list
type RankItemstruct {
 Namestring
 Scoreint64
}

// RankList maintains a list of ranked items, it's the context of
// the strategy pattern, it will use the proper strategy to sort the
// items.
type RankListstruct {
 capacityint
 items []RankItem
 sorter Sorter
}

func(r *RankList) AddItem(namestring, scoreint64) {
 r.items =append(r.items, RankItem{name, score})

 r.sorter.Sort(r.items)

iflen(r.items) >= r.capacity {
 r.items = r.items[:r.capacity]
 }
}

func(r *RankList) Items() []RankItem {
return r.items
}

運行測試以演示:

package strategy_test

import (
"strategy"
"testing"
)

funcTestStrategy(t *testing.T) {
 basketballScores := []strategy.RankItem{
 {Name:"John", Score:100},
 {Name:"Bob", Score:50},
 {Name:"Alice", Score:60},
 {Name:"Carol", Score:70},
 }
 basketballScoreBoard := strategy.NewRankList(10, &strategy.InsertSort{})
for _, v :=range basketballScores {
 basketballScoreBoard.AddItem(v.Name, v.Score)
 }
 t.Logf("basketball scoreboard: %v", basketballScoreBoard.Items())

 marathonScores := []strategy.RankItem{
 {Name:"John", Score:100},
 {Name:"Bob", Score:50},
 {Name:"Alice", Score:60},
 {Name:"Carol", Score:70},
 {Name:"David", Score:80},
 {Name:"Eric", Score:90},
 {Name:"Frank", Score:80},
 {Name:"George", Score:90},
 {Name:"Harry", Score:80},
 {Name:"Ivan", Score:80},
 {Name:"Jerry", Score:80},
 {Name:"Kevin", Score:80},
 {Name:"Larry", Score:80},
 {Name:"Mary", Score:80},
 }
 marathonScoresBoard := strategy.NewRankList(20, &strategy.QuickSort{})
for _, v :=range marathonScores {
 marathonScoresBoard.AddItem(v.Name, v.Score)
 }
 t.Logf("marathon scoreboard: %v", marathonScoresBoard.Items())
}
Running tool: /opt/go/bin/gotest -timeout 30s -run ^TestStrategy$ strategy -v -count=1 -timeout=1m

=== RUN TestStrategy
 strategy_test.go:19: basketball scoreboard: [{John 100} {Carol 70} {Alice 60} {Bob 50}]
 strategy_test.go:41: marathon scoreboard: [{John 100} {Eric 90} {George 90} {David 80} {Frank 80} {Harry 80} {Ivan 80} {Jerry 80} {Kevin 80} {Larry 80} {Mary 80} {Carol 70} {Alice 60} {Bob 50}]
--- PASS: TestStrategy (0.00s)
PASS
ok strategy 0.001s

ps:此處我們優先考慮可讀性,不會太關注編碼標準,如註釋、camelCase 類型名等。我們將多個文件的代碼組織到一個 codeblock 中僅僅是爲了方便閱讀,如果您想測試可以通過 git 下載源碼 github.com/hitzhangjie/go-patterns。

策略模式的變體:

與其他模式關係:

本文總結:

策略模式封裝了可互換的算法,並將客戶端與所使用的策略實現細節解耦。它允許在運行時切換策略以改變行爲。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Q30YsWnjT-t8cVSheGhfVA