GoLang設計模式15 – 策略模式

策略模式是一種行為型設計模式。通過策略模式,可以在運行時修改一個對象的行為。

接下來仍然是通過例子來了解策略模式。比如說記憶體快取,這是我們在開發中經常使用的東西,大家應該都有一定的了解,接下來就用記憶體快取來說明下如何使用策略模式。

向記憶體里存東西對於GoLang來說算是比較簡單的事情,通過Map就可以做到,不過還是建議創建一個Cache struct來稍稍封裝一下。不過我們都知道,記憶體快取佔用的空間是有上限的。當快達到上限時,就需要清理一下快取中已有的內容。如下是清理快取的一些常見的演算法:

  • LRU(Least Recently Used):清理最近使用的最少的那些快取數據
  • FIFO(First In First Out):清理最早放入快取的那些數據
  • LFU(Least Frequently Used):清理使用頻率最低的那部分數據

現在的問題是如何將Cache和清理演算法解耦,這樣在運行時就可以調整清理快取的演算法了。但也要注意,在添加新的清理演算法時,不應該改動Cache。這時候就需要用到策略模式了。策略模式建議為相同業務的各種演算法創建一個演算法組,然後將每種演算法都封裝起來,使之有相同的介面,並且不同演算法之間可以互換。清理快取的演算法的介面就可以命名為:evictionAlgo

然後將evictionAlgo介面嵌入Cache中就可以了。

不同於讓Cache直接自己繼承evictionAlgo介面,現在可以通過evictionAlgo介面來組裝不同的清理演算法。因為evictionAlgo是一個介面,這樣在運行的時候就可以將之賦值為LRU、FIFO或LFU,而不需要對Cache struct做任何調整。

現在捋一下什麼時候使用策略模式:

  1. 當一個對象需要提供不同的行為,而又不想在運行時修改對象時
  2. 當想在運行時選擇不同的行為而又不想寫大量的條件語句時
  3. 當為同一種行為準備了不同的演算法時

下面是策略模式的UML類圖:

這裡是我們當前這個記憶體快取案例的UML圖:

具體程式碼如下:

evictionAlgo.go:

type evictionAlgo interface {
	evict(c *cache)
}

lru.go:

import "fmt"

type lru struct {
}

func (l *lru) evict(c *cache) {
	fmt.Println("Evicting by lru strategy")
}

fifo.go:

import "fmt"

type fifo struct {
}

func (l *fifo) evict(c *cache) {
	fmt.Println("Evicting by fifo strategy")
}

lfu.go

import "fmt"

type lfu struct {
}

func (l *lfu) evict(c *cache) {
	fmt.Println("Evicting by lfu strategy")
}

cache.go

type cache struct {
	storage      map[string]string
	evictionAlgo evictionAlgo
	capacity     int
	maxCapacity  int
}

func initCache(e evictionAlgo) *cache {
	storage := make(map[string]string)
	return &cache{
		storage:      storage,
		evictionAlgo: e,
		capacity:     0,
		maxCapacity:  2,
	}
}

func (c *cache) setEvictionAlgo(e evictionAlgo) {
	c.evictionAlgo = e
}

func (c *cache) add(key, value string) {
	if c.capacity == c.maxCapacity {
		c.evict()
	}
	c.capacity++
	c.storage[key] = value
}

func (c *cache) get(key string) {
	delete(c.storage, key)
}

func (c *cache) evict() {
	c.evictionAlgo.evict(c)
	c.capacity--
}

main.go

func main() {
	lfu := &lfu{}
	cache := initCache(lfu)
	cache.add("a", "1")
	cache.add("b", "2")
	cache.add("c", "3")
	lru := &lru{}
	cache.setEvictionAlgo(lru)
	cache.add("d", "4")
	fifo := &fifo{}
	cache.setEvictionAlgo(fifo)
	cache.add("e", "5")
}

執行後的輸出內容為:

Evicting by lfu strategy
Evicting by lru strategy
Evicting by fifo strategy

程式碼已上傳至GitHub: zhyea / go-patterns / strategy-pattern

END!!