SOLDES de printemps
Stratégie

Stratégie en Go

La Stratégie est un patron de conception comportemental qui transforme un ensemble de comportements en objets, et les rend interchangeables à l’intérieur de l’objet du contexte original.

L’objet original, que l’on appelle contexte, garde une référence vers un objet stratégie et lui délègue l’exécution du comportement. Les autres objets doivent remplacer l’objet stratégie associé afin de modifier la manière dont le contexte fonctionne.

Exemple conceptuel

Supposons que vous voulez mettre en place une mémoire cache. Puisqu’il s’agit de mémoire, nous savons que sa taille est limitée. Lorsqu’elle atteint sa capacité maximale, certains enregistrements doivent être effacés pour libérer de l’espace. Ceci peut être mis en place grâce à plusieurs algorithmes. En voici quelques-uns parmi les plus populaires :

  • Least Recently Used (LRU) : supprime les données qui ont été utilisées le moins récemment.
  • First In, First Out (FIFO) : supprime les données qui ont été créées en premier.
  • Least Frequently Used (LFU) : supprime les données les moins fréquemment utilisées.

Le problème qui se pose est de pouvoir découpler notre classe cache de ces algorithmes, afin de pouvoir changer l’algorithme à l’exécution. De plus, la classe cache ne doit pas être modifiée lorsqu’un nouvel algorithme est ajouté.

C’est ici que le patron de conception stratégie entre en jeu. Il nous propose de créer une famille d’algorithmes et que chaque algorithme dispose de sa propre classe. Chacune de ces classes va suivre la même interface, ce qui rend l’algorithme interchangeable à l’intérieur de la famille. Appelons notre interface commune evictionAlgo.

Notre classe cache principale va imbriquer l’interface evictionAlgo. Plutôt que d’implémenter tous les types des algorithmes d’éviction, notre classe cache doit tout déléguer à l’interface evictionAlgo. Étant donné que evictionAlgo est une interface, nous pouvons modifier l’algorithme à l’exécution sans toucher à la classe cache, que ce soit pour LRU, FIFO ou LFU.

evictionAlgo.go: Interface stratégie

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: Stratégie concret

package main

import "fmt"

type Lru struct {
}

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

lfu.go: Stratégie concret

package main

import "fmt"

type Lfu struct {
}

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

cache.go: Contexte

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: Code client

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: Résultat de l’exécution

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

Stratégie dans les autres langues

Stratégie en C# Stratégie en C++ Stratégie en Java Stratégie en PHP Stratégie en Python Stratégie en Ruby Stratégie en Rust Stratégie en Swift Stratégie en TypeScript