Autumn SALE
Strategy

Strategy en Go

Strategy es un patrón de diseño de comportamiento que convierte un grupo de comportamientos en objetos y los hace intercambiables dentro del objeto de contexto original.

El objeto original, llamado contexto, contiene una referencia a un objeto de estrategia y le delega la ejecución del comportamiento. Para cambiar la forma en que el contexto realiza su trabajo, otros objetos pueden sustituir el objeto de estrategia actualmente vinculado, por otro.

Ejemplo conceptual

Supongamos que estás creando una memoria caché. Debido a que está en la memoria, tiene un tamaño limitado. Cuando alcanza su tamaño máximo, deben eliminarse algunas entradas para liberar espacio. Esto puede suceder a través de varios algoritmos. Algunos de los algoritmos populares son:

  • Menos usada recientemente (LRU, por sus siglas en inglés): elimina una entrada que se ha utilizado menos recientemente.
  • Primera en entrar, primera en salir (FIFO): elimina una entrada que se creó primero.
  • Menos frecuentemente usada (LFU): elimina una entrada que se ha utilizado menos frecuentemente.

El problema está en cómo desacoplar nuestras clases caché de estos algoritmos para que podamos cambiar el algoritmo durante el tiempo de ejecución. Además, la clase caché no debe cambiar cuando se añade un nuevo algoritmo.

Aquí es donde el patrón Strategy entra en escena. Sugiere la creación de una familia del algoritmo en la que cada algoritmo tiene su propia clase. Cada una de estas clases sigue la misma interfaz, y esto hace que el algoritmo sea intercambiable dentro de la familia. Digamos que el nombre de la interfaz común es evictionAlgo.

Ahora nuestra clase caché principal integrará la interfaz evictionAlgo. En lugar de implementar todos los tipos de algoritmos de reemplazo en sí mismos, nuestra clase caché delegará todo ello a la interfaz evictionAlgo. Ya que evictionAlgo es una interfaz, podemos cambiar el algoritmo durante el tiempo de ejecución a LRU, FIFO, LFU sin realizar cambios en la clase caché.

evictionAlgo.go: Interfaz de estrategia

package main

type EvictionAlgo interface {
	evict(c *Cache)
}

fifo.go: Concrete strategy

package main

import "fmt"

type Fifo struct {
}

func (l *Fifo) evict(c *Cache) {
	fmt.Println("Evicting by fifo strtegy")
}

lru.go: Estrategia concreta

package main

import "fmt"

type Lru struct {
}

func (l *Lru) evict(c *Cache) {
	fmt.Println("Evicting by lru strtegy")
}

lfu.go: Estrategia concreta

package main

import "fmt"

type Lfu struct {
}

func (l *Lfu) evict(c *Cache) {
	fmt.Println("Evicting by lfu strtegy")
}

cache.go: Contexto

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: Resultado de la ejecución

Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy

Strategy en otros lenguajes

Strategy en C# Strategy en C++ Strategy en Java Strategy en PHP Strategy en Python Strategy en Ruby Strategy en Rust Strategy en Swift Strategy en TypeScript