
Strategy em Go
O Strategy é um padrão de projeto comportamental que transforma um conjunto de comportamentos em objetos e os torna intercambiáveis dentro do objeto de contexto original.
O objeto original, chamado contexto, mantém uma referência a um objeto strategy e o delega a execução do comportamento. Para alterar a maneira como o contexto executa seu trabalho, outros objetos podem substituir o objeto strategy atualmente vinculado por outro.
Exemplo conceitual
Suponha que você esteja criando um cache na memória. Por estar na memória, seu tamanho é limitado. Sempre que atinge seu tamanho máximo, algumas entradas precisam ser despejadas para liberar espaço. Isso pode acontecer por meio de vários algoritmos. Alguns dos algoritmos populares são:
- Least Recently Used (LRU - Menos Usada Recentemente): remove uma entrada que foi menos usada recentemente.
- First In, First Out (FIFO - Primeira a entrar, primeira a sair): remove uma entrada que foi criada primeiro.
- Least Frequently Used (LFU - Menos Usada Frequentemente): remove uma entrada que foi usada com menos frequência.
O problema é como desacoplar nossa classe de cache desses algoritmos para que possamos alterar o algoritmo em tempo de execução. Além disso, a classe de cache não deve ser alterada quando um novo algoritmo está sendo adicionado.
É aqui que o padrão Strategy entra em cena. Ele sugere a criação de uma família do algoritmo com cada algoritmo tendo sua própria classe. Cada uma dessas classes segue a mesma interface, e isso torna o algoritmo intercambiável dentro da família. Digamos que o nome comum da interface seja evictionAlgo
.
Agora nossa classe de cache principal irá incorporar a interface evictionAlgo
. Em vez de implementar todos os tipos de algoritmos de remoção em si, nossa classe de cache irá delegar tudo para a interface evictionAlgo
. Como evictionAlgo
é uma interface, podemos executar a alteração do algoritmo em tempo de execução para LRU, FIFO, LFU sem qualquer alteração na classe de cache.
evictionAlgo.go: Interface de estratégia
package main
type evictionAlgo interface {
evict(c *cache)
}
fifo.go: Estratégia concreta
package main
import "fmt"
type fifo struct {
}
func (l *fifo) evict(c *cache) {
fmt.Println("Evicting by fifo strtegy")
}
lru.go: Estratégia concreta
package main
import "fmt"
type lru struct {
}
func (l *lru) evict(c *cache) {
fmt.Println("Evicting by lru strtegy")
}
lfu.go: Estratégia concreta
package main
import "fmt"
type lfu struct {
}
func (l *lfu) evict(c *cache) {
fmt.Println("Evicting by lfu strtegy")
}
cache.go: Context
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: Código cliente
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: Resultados da execução
Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy