![Strategia](/images/patterns/cards/strategy-mini.png?id=d38abee4fb6f2aed909d262bdadca936)
Strategia w języku Go
Strategia to behawioralny wzorzec projektowy zakładający przekształcenie zestawu zachowań w obiekty, które można stosować zamiennie w pierwotnym obiekcie.
Pierwotny obiekt, zwany kontekstem, przechowuje odniesienie do obiektu-strategii i deleguje mu działania związane z danym zachowaniem. Aby zmienić sposób, w jaki kontekst wykonuje swą pracę, należy zamienić bieżąco przypisany obiekt strategii na inny.
Przykład koncepcyjny
Przypuśćmy, że tworzymy schowek w obrębie pamięci. Skoro znajduje się w pamięci, to ma pewien ograniczony rozmiar. Gdy ilość danych w schowku wyczerpie dostępną pamięć, niektóre wpisy muszą zostać z niego usunięte aby zwolnić miejsce. Może się to odbywać według kilku algorytmów, z których najpopularniejsze to:
- Najrzadziej ostatnio używane (LRU): usuwa wpis który był ostatnio najrzadziej potrzebny.
- Pierwszy na wejściu, pierwszy na wyjściu (FIFO): usuwa najwcześniejszy wpis.
- Najrzadziej używane (LFU): usuwa najrzadziej używany wpis.
Problemem jest teraz rozsprzęgnięcie naszej klasy pamięci podręcznej i powyższych algorytmów tak, aby można było wybrać odpowiedni w trakcie działania programu. Ponadto, klasa pamięci podręcznej nie powinna ulegać zmianom gdy dodawany jest nowy algorytm.
Tu właśnie z pomocą przychodzi nam wzorzec Strategia. Proponuje on utworzenie rodziny algorytmów z których każdy z nich będzie osobną klasą. Każda z tych klas będzie zgodna pod względem interfejsu, co pozwoli na wymianę jednego algorytmu na inny. Powiedzmy, że nazwą wspólnego interfejsu jest evictionAlgo
.
Od tego momentu nasza główna klasa pamięci podręcznej będzie zawierała interfejs evictionAlgo
. Zamiast implementować wszystkie algorytmy zwalniania przestrzeni w klasie, będzie ona delegować te zadania interfejsowi evictionAlgo
. Skoro evictionAlgo
jest interfejsem, możemy wybrać stosowany algorytm (LRU, FIFO, LFU) w trakcie działania programu bez konieczności zmiany w klasie pamięci podręcznej.
evictionAlgo.go: Interfejs strategii
package main
type EvictionAlgo interface {
evict(c *Cache)
}
fifo.go: Konkretna strategia
package main
import "fmt"
type Fifo struct {
}
func (l *Fifo) evict(c *Cache) {
fmt.Println("Evicting by fifo strtegy")
}
lru.go: Konkretna strategia
package main
import "fmt"
type Lru struct {
}
func (l *Lru) evict(c *Cache) {
fmt.Println("Evicting by lru strtegy")
}
lfu.go: Konkretna strategia
package main
import "fmt"
type Lfu struct {
}
func (l *Lfu) evict(c *Cache) {
fmt.Println("Evicting by lfu strtegy")
}
cache.go: Kontekst
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: Kod klienta
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: Wynik działania
Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy