Стратегия в Go
Стратегия – это поведенческий паттерн, который превращает набор поведений в объекты и делает их взаимозаменяемыми внутри исходного контекстного объекта.
Концептуальный пример
Предположим, что вы создаете In-Memory-Cache. Поскольку он находится в памяти, его размер ограничен. Когда он достигает максимального размера, некоторые записи должны быть удалены, чтобы освободить место. Это может происходить по нескольким алгоритмам. Вот некоторые из популярных алгоритмов:
- Наименее часто используемая запись (LRU): удаление записи, которая использовалась меньше всего.
- First In, First Out (FIFO): удаление записи, которая была создана первой.
- Least Frequently Used (LFU): удаление записи, которая использовалась реже всего.
Проблема заключается в том, как отвязать наш класс кэша от этих алгоритмов, чтобы мы могли менять алгоритм во время выполнения. Кроме того, класс кэша не должен меняться при добавлении нового алгоритма.
Здесь на помощь приходит паттерн Strategy. Он предполагает создание семейства алгоритмов, в котором каждый алгоритм имеет свой класс. Каждый из этих классов использует один и тот же интерфейс, что делает алгоритм взаимозаменяемым внутри семейства. Допустим, имя общего интерфейса – evictionAlgo.
Теперь в наш основной класс кэша будет встроен интерфейс evictionAlgo. Вместо того чтобы самому реализовывать все типы алгоритмов выселения, наш класс кэша будет делегировать их выполнение интерфейсу evictionAlgo. Поскольку evictionAlgo является интерфейсом, мы можем менять алгоритм во время выполнения на LRU, FIFO, LFU без изменения класса кэша.
evictionAlgo.go: Интерфейс стратегии
package main
type EvictionAlgo interface {
evict(c *Cache)
}
fifo.go: Конкретная стратегия
package main
import "fmt"
type Fifo struct {
}
func (l *Fifo) evict(c *Cache) {
fmt.Println("Evicting by fifo strtegy")
}
lru.go: Конкретная стратегия
package main
import "fmt"
type Lru struct {
}
func (l *Lru) evict(c *Cache) {
fmt.Println("Evicting by lru strtegy")
}
lfu.go: Конкретная стратегия
package main
import "fmt"
type Lfu struct {
}
func (l *Lfu) evict(c *Cache) {
fmt.Println("Evicting by lfu strtegy")
}
cache.go: Контекст
package main
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: Код клиента
package main
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")
}
output.txt: Результат выполнения
Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy