본문 바로가기
golang/design pattern

golang design pattern #1 Builder

by PudgeKim 2021. 11. 10.

Builder 패턴을 알아보기전에 아래 링크를 참고하여 golang의 type embedding 문법을 보고 오시길 바랍니다.

https://up-to-date-items.tistory.com/114

 

golang의 is a 관계

이번 글에서는 golang의 is a 관계에 대해 알아보겠습니다. 예시 보는게 이해가 빠르므로 바로 예시로 넘어가겠습니다. 위 코드를 보다시피 일반 Player에는 bonus가 없고 Pro에게만 bonus가 있습니다.

up-to-date-items.tistory.com

 

 

이제 Builder 패턴을 알아보겠습니다.

 

1
2
3
4
5
6
7
8
9
type Person struct {
    // 주소 관련
    Address string
    PostCode string
 
    // 직업 관련
    JobType string
    Company string
}
cs

위처럼 Person 구조체가 있다면 해당 구조체를 선언하려면 아래처럼 필요한 인자들을 나열해야 합니다.

1
2
3
4
5
6
p := Person{
        Address: "Seoul...",
        PostCode: "14931",
        JobType: "IT",
        Company: "Google",
    }
cs

예제의 간략화를 위해 구조체에 4개의 필드만 지정했지만 훨씬 더 많다면 보기에도 불편하고 선언시에도 불편합니다.

Builder 패턴을 이용하면 조금 더 직관적이고 각 필드마다 독립적인 기능을 추가할 수도 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
type PersonBuilder struct {
    person *Person
}
 
// PersonBuilder의 receiver function들을 사용할 수 있음
type PersonJobBuilder struct {
    PersonBuilder
}
 
// PersonBuilder의 receiver function들을 사용할 수 있음
type PersonAddressBuilder struct {
    PersonBuilder
}
cs

우선 직업 관련 필드들을 담당할 구조체와 주소 관련 필드들을 나누기 위해 위처럼 구조체를 분리하였습니다.

또한, 위 코드의 주석에 적힌대로 PersonJobBuilder와 PersonAddressBuilder는 PersonBuilder의 메서드들을 사용할 수 있습니다.
(이에 대한 설명은 맨 위 golang의 type embedding 링크에 있습니다.)

이제 위 3개의 구조체들의 메서드들을 작성해보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type PersonBuilder struct {
    person *Person
}
 
func NewPersonBuilder() *PersonBuilder {
    // 각 필드는 비워둔채로 초기화함
    return &PersonBuilder{&Person{}}
}
 
func (pb *PersonBuilder) Build() *Person {
    return pb.person
}
 
func (pb *PersonBuilder) Work() *PersonJobBuilder {
    return &PersonJobBuilder{*pb}
}
 
func (pb *PersonBuilder) Live() *PersonAddressBuilder {
    return &PersonAddressBuilder{*pb}
}
cs

먼저 PersonBuilder 타입의 메서드들입니다.

Build 메서드는 나중에 Person type을 인자로 받는 함수들을 위해 만들어진 것이고
Work와 Live 메서드는 보다시피 각각의 구조체를 return 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
type PersonJobBuilder struct {
    PersonBuilder
}
 
func (pjb *PersonJobBuilder) At(company string) *PersonJobBuilder {
    pjb.person.Company = company
    return pjb
}
 
func (pjb *PersonJobBuilder) JobType(jobType string) *PersonJobBuilder {
    pjb.person.JobType = jobType
    return pjb
}
cs

다음은 PersonJobBuilder의 메서드들입니다.

각 메서드들을 통해 필드를 설정할 수 있습니다. 위에서는 정말 set 기능만 하였지만 각 함수내에서 여러가지 조건을 넣어서 사용자의 실수를 방지할 수 있습니다.

중요한 부분은 각 메서드마다 *PersonJobBuilder 타입을 return한 것인데 이렇게 return 함으로써 chaining이 가능해집니다.

1
2
3
4
pb := NewPersonBuilder()
pb.Work()
  .JobType("IT")
  .At("Google")
cs

위처럼 chaining이 가능해집니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
type PersonAddressBuilder struct {
    PersonBuilder
}
 
func (pab *PersonAddressBuilder) At(address string) *PersonAddressBuilder {
    pab.person.Address = address
    return pab
}
 
func (pab *PersonAddressBuilder) PostCode(postcode string) *PersonAddressBuilder {
    pab.person.PostCode = postcode
    return pab
}
cs

마지막으로 PersonAddressBuilder의 메서드들이며 마찬가지로 chaining이 가능하게 하였습니다.

 

이렇게 만들어진 코드들로 아래와 같은 코드가 작성가능해집니다.

1
2
3
4
5
6
7
pb := NewPersonBuilder()
 
pb.Live().At("서울시....").PostCode("15941")
      .Work().JobType("IT").At("Google")
    
person := pb.Build()
fmt.Println(person)
cs

 

이렇게 Builder 패턴을 알아보았습니다.

'golang > design pattern' 카테고리의 다른 글

golang design pattern #6 Bridge  (0) 2021.11.12
golang design pattern #5 Adapter  (0) 2021.11.12
golang design pattern #4 Singleton  (0) 2021.11.11
golang design pattern #3 Prototype  (0) 2021.11.11
golang design pattern #2 Factory  (0) 2021.11.10