Нужна клёвая книга о паттернах на русском? Вот она »
Стратегия

Стратегия на Go

Стратегия — это поведенческий паттерн, выносит набор алгоритмов в собственные классы и делает их взаимозаменимыми.

Другие объекты содержат ссылку на объект-стратегию и делегируют ей работу. Программа может подменить этот объект другим, если требуется иной способ решения задачи.

Концептуальный пример

Представьте, что вы разрабатываете «In-Memory-Cache». Поскольку он находится внутри памяти, его размер ограничен. Как только он полностью заполнится, какие-то записи придется убрать для освобождения места. Эту функцию можно реализовать с помощью нескольких алгоритмов, самые популярные среди них:

  • Наиболее давно использовавшиеся (Least Recently Used – LRU): убирает запись, которая использовалась наиболее давно.
  • «Первым пришел, первым ушел» (First In, First Out — FIFO): убирает запись, которая была создана раньше остальных
  • Наименее часто использовавшиеся (Least Frequently Used — LFU): убирает запись, которая использовалась наименее часто.

Проблема заключается в том, чтобы отделить кэш от этих алгоритмов для возможности их замены «на ходу». Помимо этого, класс кэша не должен меняться при добавлении нового алгоритма.

В такой ситуации нам поможет паттерн Стратегия. Он предполагает создание семейства алгоритмов, каждый из которых имеет свой класс. Все классы применяют одинаковый интерфейс, что делает алгоритмы взаимозаменяемыми внутри этого семейства. Назовем этот общий интерфейс evictionAlgo.

Теперь основной класс нашего кэша будет включать в себя evictionAlgo. Вместо прямой реализации всех типов алгоритмов вытеснения внутри самого себя, наш класс будет передавать их в evictionAlgo. Поскольку это интерфейс, мы можем непосредственно во время выполнения программы менять алгоритм на LRU, FIFO, LFU без изменений в классе кэша.

evictionAlgo.go: Интерфейс стратегии

package main

type evictionAlgo interface {
	evict(c *cache)
}

fifo.go: Конкретная стратегия

package main

import "fmt"

type fifo struct {
}

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

lru.go: Конкретная стратегия

package main

import "fmt"

type lru struct {
}

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

lfu.go: Конкретная стратегия

package main

import "fmt"

type lfu struct {
}

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

cache.go: Контекст

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: Клиентский код

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: Результат выполнения

Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy
По материалам: Golang By Example

Стратегия на других языках программирования

Стратегия на Java Стратегия на C# Стратегия на C++ Стратегия на PHP Стратегия на Python Стратегия на Ruby Стратегия на Swift Стратегия на TypeScript