프로토타입 패턴
프로토타입 패턴(prototype pattern)은 생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정되도록 하며, 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 된다.
얕은 복사(shallow copy) vs 깊은 복사(deep copy)
얕은 복사
객체를 복사할때 참조값을 가진 멤버는 참조값만 복사 됨.
type Status struct {
str int
dex int
wis int
}
type Monster struct {
Name string
Status *Status
}
func main() {
slime := Monster{
"slime",
&Status{1,1,1},
}
goblin := slime
goblin.Name = "goblin"
goblin.Status.str = 3
fmt.Println(slime, slime.Status)
fmt.Println(goblin, goblin.Status)
}
실행결과
{slime 0xc0000141a0} &{3 1 1}
{goblin 0xc0000141a0} &{3 1 1}
=> Status의 참조값만 복사되었기때문에 고블린의 스탯을 변경하면 슬라임의 스탯도 같이 변경된다.
깊은 복사
type Status struct {
Str int
Dex int
Wis int
}
type Monster struct {
Name string
Status *Status
}
func (m *Monster) DeepCopy() *Monster {
return &Monster{
m.Name,
&Status{
m.Status.Str,
m.Status.Dex,
m.Status.Wis,
},
}
}
func main() {
slime := &Monster{
"slime",
&Status{1, 1, 1},
}
goblin := slime.DeepCopy()
goblin.Name = "goblin"
goblin.Status.Str = 3
fmt.Println(slime, slime.Status)
fmt.Println(goblin, goblin.Status)
}
실행결과
{slime 0xc0000141a0} &{1 1 1}
{goblin 0xc0000141a0} &{3 1 1}
=> Status의 참조값이 아닌 참조된 객체의 내용을 복사했기때문에 고블린의 Status를 변경해도 슬라임의 Status에는 영향이 없음.
직렬화(serialization)를 통한 깊은 복사
type Status struct {
Str int
Dex int
Wis int
}
type Monster struct {
Name string
Status *Status
}
func (m *Monster) DeepCopy() *Monster {
b := bytes.Buffer{}
e := gob.NewEncoder(&b)
_ = e.Encode(m)
d := gob.NewDecoder(&b)
monster := Monster{}
_ = d.Decode(&monster)
return &monster
}
func main() {
slime := &Monster{
"slime",
&Status{1, 1, 1},
}
goblin := slime.DeepCopy()
goblin.Name = "goblin"
goblin.Status.Str = 3
fmt.Println(slime, slime.Status)
fmt.Println(goblin, goblin.Status)
}
실행 결과
&{slime 0xc0000141c0} &{1 1 1}
&{goblin 0xc000014460} &{3 1 1}
prototype factory
- 현재 아래코드에서 스킬 배열이 복사가 안됨 => 원인파악중
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Status struct {
Str int
Dex int
Wis int
}
type Monster struct {
Name string
Status *Status
Skills []string
}
func (m *Monster) DeepCopy() *Monster {
b := bytes.Buffer{}
e := gob.NewEncoder(&b)
_ = e.Encode(m)
d := gob.NewDecoder(&b)
monster := Monster{}
_ = d.Decode(&monster)
return &monster
}
// prototype
var (
slime = Monster{"slime", &Status{1, 1, 1}, []string{}}
goblin = Monster{"goblin", &Status{3, 1, 1}, []string{}}
)
func NewMonster(proto *Monster, name string, skills []string) *Monster {
monster := proto.DeepCopy()
monster.Name = name
copy(monster.Skills, skills)
return monster
}
func main() {
normalSlime := NewMonster(&slime, "평범한 슬라임", []string{})
normalGoblin := NewMonster(&goblin, "평범한 고블린", []string{})
fireGoblin := NewMonster(&goblin, "화염 고블린", []string{"파이어볼"})
fmt.Println(normalSlime, normalSlime.Status)
fmt.Println(normalGoblin, normalGoblin.Status)
fmt.Println(fireGoblin, fireGoblin.Status)
}