봄맞이 세일
전략

Go로 작성된 전략

전략은 행동들의 객체들을 객체들로 변환하며 이들이 원래 콘텍스트 객체 내에서 상호 교환이 가능하게 만드는 행동 디자인 패턴입니다.

원래 객체는 콘텍스트라고 불리며 전략 객체에 대한 참조를 포함합니다. 콘텍스트는 행동의 실행을 연결된 전략 객체에 위임합니다. 콘텍스트가 작업을 수행하는 방식을 변경하기 위하여 다른 객체들은 현재 연결된 전략 객체를 다른 전략 객체와 대체할 수 있습니다.

개념적인 예시

당신이 인메모리 캐싱을 생성하고 있다고 가정해 봅시다. 이것은 메모리 내에 있으므로 크기가 제한되며 최대 크기에 도달할 때마다 공간을 확보하기 위해 일부 항목들을 제거해야 합니다. 위 사건은 여러 알고리즘을 통해 발생할 수 있으며 다음은 일부 인기 있는 알고리즘들입니다:

  • LRU 알고리즘: LRU는 Least Recently Used의 약자이며 해당 알고리즘은 가장 최근에 사용된 항목을 제거합니다.
  • FIFO 알고리즘: FIFO는 First In First Out의 약자이며 해당 알고리즘은 가장 처음에 생성된 항목을 제거합니다.
  • LFU 알고리즘: LFU는 Least Frequently Used의 약자이며 해당 알고리즘은 가장 적게 사용된 항목을 제거합니다.

이제 우리가 해결해야 할 문제는 런타임에 알고리즘을 변경할 수 있도록 캐시 클래스를 이러한 알고리즘에서 분리하는 방법입니다. 또한 새 알고리즘이 추가될 때 캐시 클래스가 변경되지 않아야 합니다.

이러한 상황에서 전략 패턴이 유용해집니다. 전략 패턴은 각 알고리즘에 해당 알고리즘의 클래스가 있는 알고리즘 패밀리를 만들 것을 제안합니다. 이러한 각 클래스는 같은 인터페이스를 따르므로 패밀리 내에서 알고리즘을 교환할 수 있습니다. 일반적인 인터페이스 이름이 eviction­Algo라고 가정해 보겠습니다.

이제 우리의 주 캐시 클래스는 evictionAlgo 인터페이스를 포함합니다. 모든 유형의 eviction 알고리즘들을 자체적으로 구현하는 대신 해당 캐시 클래스는 실행을 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

다른 언어로 작성된 전략

C#으로 작성된 전략 C++로 작성된 전략 자바로 작성된 전략 PHP로 작성된 전략 파이썬으로 작성된 전략 루비로 작성된 전략 러스트로 작성된 전략 스위프트로 작성된 전략 타입스크립트로 작성된 전략