Viva! A versão em Português Brasileiro finalmente foi lançada! Por favor, envie-nos uma mensagem se você quer compartilhar sua opinião ou relatar um erro.
Strategy

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
Baseado em: Golang By Example

Strategy em outras linguagens

Padrões de Projeto: Strategy em Java Padrões de Projeto: Strategy em C# Padrões de Projeto: Strategy em C++ Padrões de Projeto: Strategy em PHP Padrões de Projeto: Strategy em Python Padrões de Projeto: Strategy em Ruby Padrões de Projeto: Strategy em Swift Padrões de Projeto: Strategy em TypeScript