Wiosenna WYPRZEDAŻ
Strategia

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
Na podstawie: Golang By Example

Strategia w innych językach

Strategia w języku C# Strategia w języku C++ Strategia w języku Java Strategia w języku PHP Strategia w języku Python Strategia w języku Ruby Strategia w języku Rust Strategia w języku Swift Strategia w języku TypeScript