Стратегия в 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
+1
0
+1
2
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *