Потрібна крута книжка про патерни та ще й українською? Ось вона »
Стратегія

Стратегія на 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