Go에서는 Python __init__()
과 같은 명시적인 생성자가 없습니다. 그렇기 때문에 일반적으로 객체를 생성할 때는 함수의 네이밍을 New + <StructName>
로 구성하면서 이 함수가 생성자임을 나타냅니다. 아래는 하나의 객체를 생성하는 간단한 예시입니다. 필요한 매개변수를 함수에 직접 전달하는 방식으로 객체의 속성의 값을 설정하고 생성할 수 있습니다.
type Person struct {
FirstName string
LastName string
}
func NewPerson(firstName, lastName string) *Person {
return &Person{
FirstName: firstName,
LastName: lastName,
}
}
func main() {
person := NewPerson("John", "Doe")
fmt.Println(person)
}
간단한 객체를 생성할 때는 위와 같이 직접 전달하여 생성하는 것이 직관적이고 간결합니다. 하지만 Struct에 속성이 늘어난다면 어떨까요 ? 간단한 예시입니다.
type Person struct {
FirstName string
LastName string
Age int
Gender string
Address string
}
func NewPerson(firstName, lastName, gender, address string, age int) *Person {
return &Person{
FirstName: firstName,
LastName: lastName,
Age: age,
Gender: gender,
Address: address,
}
}
func main() {
person := NewPerson("John", "Doe", "male", "New York", 25)
fmt.Println(person)
}
물론 위와 같이 모든 매개변수를 전달하여 생성할 수도 있지만, 우리는 몇 가지 질문이 떠오릅니다.
등등 여러 고민에 맞닥뜨리게 됩니다. 여기서 이러한 문제를 보완하는 것이 Options Pattern 입니다.
Options Pattern: 객체 생성 시, 필요한 설정만 선택적으로 사용하여 객체를 생성하는 패턴
선택적으로 사용한다는 것은 Setter를 구현하고 이를 사용하는 것입니다. Go에서 구조체의 속성을 설정하기 위한 Setter 함수의 네이밍은 일반적으로 With~ 혹은 Set~ 의 네이밍을 주로 사용합니다. (Options Pattern에서는 좀 더 With~ 네이밍을 사용하는 것 같네요 .. ?)
func WithFirstName(firstName string) func(*Person) {
return func(p *Person) {
p.FirstName = firstName
}
}
func WithLastName(lastName string) func(*Person) {
return func(p *Person) {
p.LastName = lastName
}
}
func WithAge(age int) func(*Person) {
return func(p *Person) {
p.Age = age
}
}
위의 함수들의 시그니처를 보게되면, With + <속성>
의 네이밍을 가지고 있고, 속성에 설정할 값을 매개변수로 받고 있습니다. 응답 타입은 포인터 구조체를 매개변수로 받는 함수이고, 그 함수를 보게되면, 구조체의 특정 속성을 매개변수로 설정하고 있습니다.
그리고 이를 호출하고 활용하는 방법은 아래와 같습니다. 생성자 함수를 보게 되면 이전에는 각 속성을 직접적인 매개변수로 받았지만, Options Pattern을 적용한 이후 Setter 함수들을 매개변수로 받으면서 내부에서 그 함수들을 순회하고, 최종 객체를 반환합니다.
func NewPerson(opts ...func(*Person)) *Person {
p := &Person{}
for _, opt := range opts {
opt(p)
}
return p
}
func main() {
person := NewPerson(
WithFirstName("Jin"),
WithLastName("Lee"),
WithAge(30),
)
fmt.Println(person)
}
Options Pattern을 사용하면,
이러한 장점들로 객체의 복잡한 설정이 필요한 경우 자주 사용되곤합니다.