프로토타입 패턴

프로토타입 패턴(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)
}