플라이웨이트 는 구조 패턴이며 프로그램들이 객체들의 메모리 소비를 낮게 유지하여 방대한 양의 객체들을 지원할 수 있도록 합니다.
이 패턴은 여러 객체 사이의 객체 상태를 공유하여 위를 달성합니다. 다르게 설명하자면 플라이웨이트는 다른 객체들이 공통으로 사용하는 데이터를 캐싱하여 RAM을 절약합니다.
개념적인 예시
카운터-스트라이크 게임에서 테러리스트와 대테러 요원은 각각 여러 유형의 복장(dress)을 가지고 있으나 예시의 단순화를 위해 테러리스트와 대테러 요원 모두 각각 하나의 드레스 유형을 가지고 있다고 가정해 보겠습니다. 드레스 객체는 아래와 같이 플레이어 객체에 내장되어 있습니다.
아래는 플레이어를 위한 구조체(struct)이며 드레스 객체가 플레이어 구조체에 포함되어 있음을 알 수 있습니다:
type player struct {
dress dress
playerType string // Can be T or CT
lat int
long int
}
5명의 테러리스트와 5명의 대테러 요원들, 그러니까 총 10명의 플레이어가 있다고 가정해 봅시다. 이제 복장과 관련하여 두 가지 옵션이 있습니다.
10명의 플레이어 객체들은 각각 다른 복장 객체를 만든 후 포함시킵니다. 총 10개의 복장 객체들이 생성됩니다.
우리는 두 개의 복장 객체를 만듭니다:
단일 테러리스트 복장 객체: 5명의 테러리스트가 공유합니다.
단일 대테러 요원 복장 객체: 5명의 대테러 요원들이 공유합니다.
첫 번째 접근법에서는 총 10개의 복장 객체들이 생성되나 두 번째 접근법에서는 두 개의 복장 객체만 생성됩니다. 플라이웨이트 패턴은 두 번째 접근법을 따릅니다. 우리가 만든 두 개의 복장 객체들을 플라이웨이트 객체라고 합니다.
플라이웨이트 패턴은 공통부분들을 제거하고 플라이웨이트 객체들을 생성합니다. 플라이웨이트 객체들(복장)은 이제 여러 객체(플레이어) 간에 공유될 수 있습니다. 이렇게 하면 복장 객체들의 수가 크게 줄어들며, 플레이어들을 더 많이 생성하더라도 두 개의 복장 객체로 충분하다는 점입니다.
플라이웨이트 패턴에서 우리는 플라이웨이트 객체들을 맵 필드에 저장합니다. 플라이웨이트 객체들을 공유하는 다른 객체들이 생성될 때마다 플라이웨이트 객체들은 맵에서 가져와집니다.
이 구조의 어떤 부분이 고유한 상태와 공유한 상태가 되는지 봅시다.
dressFactory.go: 플라이웨이트 팩토리
package main
import "fmt"
const (
//TerroristDressType terrorist dress type
TerroristDressType = "tDress"
//CounterTerrroristDressType terrorist dress type
CounterTerrroristDressType = "ctDress"
)
var (
dressFactorySingleInstance = &DressFactory{
dressMap: make(map[string]Dress),
}
)
type DressFactory struct {
dressMap map[string]Dress
}
func (d *DressFactory) getDressByType(dressType string) (Dress, error) {
if d.dressMap[dressType] != nil {
return d.dressMap[dressType], nil
}
if dressType == TerroristDressType {
d.dressMap[dressType] = newTerroristDress()
return d.dressMap[dressType], nil
}
if dressType == CounterTerrroristDressType {
d.dressMap[dressType] = newCounterTerroristDress()
return d.dressMap[dressType], nil
}
return nil, fmt.Errorf("Wrong dress type passed")
}
func getDressFactorySingleInstance() *DressFactory {
return dressFactorySingleInstance
}
dress.go: 플라이웨이트 인터페이스
package main
type Dress interface {
getColor() string
}
terroristDress.go: 구상 플라이웨이트 객체
package main
type TerroristDress struct {
color string
}
func (t *TerroristDress) getColor() string {
return t.color
}
func newTerroristDress() *TerroristDress {
return &TerroristDress{color: "red"}
}
counterTerroristDress.go: 구상 플라이웨이트 객체
package main
type CounterTerroristDress struct {
color string
}
func (c *CounterTerroristDress) getColor() string {
return c.color
}
func newCounterTerroristDress() *CounterTerroristDress {
return &CounterTerroristDress{color: "green"}
}
player.go: 콘텍스트
package main
type Player struct {
dress Dress
playerType string
lat int
long int
}
func newPlayer(playerType, dressType string) *Player {
dress, _ := getDressFactorySingleInstance().getDressByType(dressType)
return &Player{
playerType: playerType,
dress: dress,
}
}
func (p *Player) newLocation(lat, long int) {
p.lat = lat
p.long = long
}
game.go: 클라이언트 코드
package main
type game struct {
terrorists []*Player
counterTerrorists []*Player
}
func newGame() *game {
return &game{
terrorists: make([]*Player, 1),
counterTerrorists: make([]*Player, 1),
}
}
func (c *game) addTerrorist(dressType string) {
player := newPlayer("T", dressType)
c.terrorists = append(c.terrorists, player)
return
}
func (c *game) addCounterTerrorist(dressType string) {
player := newPlayer("CT", dressType)
c.counterTerrorists = append(c.counterTerrorists, player)
return
}
main.go: 클라이언트 코드
package main
import "fmt"
func main() {
game := newGame()
//Add Terrorist
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
//Add CounterTerrorist
game.addCounterTerrorist(CounterTerrroristDressType)
game.addCounterTerrorist(CounterTerrroristDressType)
game.addCounterTerrorist(CounterTerrroristDressType)
dressFactoryInstance := getDressFactorySingleInstance()
for dressType, dress := range dressFactoryInstance.dressMap {
fmt.Printf("DressColorType: %s\nDressColor: %s\n", dressType, dress.getColor())
}
}
output.txt: 실행 결과
DressColorType: ctDress
DressColor: green
DressColorType: tDress
DressColor: red