策略模式是一種行為型設計模式。通過策略模式,可以在執行時修改一個物件的行為。
接下來仍然是通過例子來了解策略模式。比如說記憶體快取,這是我們在開發中經常使用的東西,大家應該都有一定的瞭解,接下來就用記憶體快取來說明下如何使用策略模式。
向記憶體裡存東西對於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做任何調整。
現在捋一下什麼時候使用策略模式:
- 當一個物件需要提供不同的行為,而又不想在執行時修改物件時
- 當想在執行時選擇不同的行為而又不想寫大量的條件語句時
- 當為同一種行為準備了不同的演算法時
下面是策略模式的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!!