Design Patterns | Builder

Creational pattern - Builder

Use cases:

  • technique that allows to encapsulate construction logic for an object while creating
  • break up construction of complex object into smaller parts and decople construction from its representation that can be reused to create different representations

Let’s look at the example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package main

import (
"encoding/json"
"encoding/xml"
)

type MessageFormat int

const (
JSON_FORMAT MessageFormat = iota
XML_FORMAT
)

type Message struct {
Body []byte
Format MessageFormat
}
type MessageBuilder interface {
SetRecepient(recepient string)
SetText(text string)
Build() (*Message, error)
}
type JsonBuilder struct {
recepient string
text string
}

func (jb *JsonBuilder) SetRecepient(recepient string) {
jb.recepient = recepient
}
func (jb *JsonBuilder) SetText(text string) {
jb.text = text
}
func (jb *JsonBuilder) Build() (*Message, error) {
m := make(map[string]string)
m["recepient"] = jb.recepient
m["text"] = jb.text
data, err := json.Marshal(m)
if err != nil {
return nil, err
}
return &Message{Body: data, Format: JSON_FORMAT}, nil
}

type XmlBuilder struct {
recepient string
text string
}
type XMLMessage struct {
Recipient string `xml:"recipient"`
Text string `xml:"body"`
}

func (xb *XmlBuilder) SetRecepient(recepient string) {
xb.recepient = recepient
}
func (xb *XmlBuilder) SetText(text string) {
xb.text = text
}
func (xb *XmlBuilder) Build() (*Message, error) {
m := XMLMessage{Recipient: xb.recepient, Text: xb.text}
data, err := xml.Marshal(m)
if err != nil {
return nil, err
}
return &Message{Body: data, Format: XML_FORMAT}, nil
}

type Sender struct {
}

func (s *Sender) BuildMessage(mb MessageBuilder) (*Message, error) {
mb.SetRecepient("Santa Claus")
mb.SetText("I was a good body, give me a gift!")
return mb.Build()
}

The main idea is next:

  • MessageFormat defines two formats of message(xml, json)
  • Message type define two fields(body of message and format)
  • MessageBuilder common interface for builder object with corresponding methods for setters and Build() function
  • JsonBuilder and XmlBuilder are concrete implementations MessageBuilder interface. It creates messages in two different formats
  • Sender is director class that creates message. It accepts a MessageBuilder type and build it. And it doesn’t really matter what exactly type of MessageBuilder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
sender := Sender{}
jsonMsg, err := sender.BuildMessage(&JsonBuilder{})
if err != nil {
panic(err)
}
fmt.Println(string(jsonMsg.Body))
xmlMsg, err := sender.BuildMessage(&XmlBuilder{})
if err != nil {
panic(err)
}
fmt.Println(string(xmlMsg.Body))
}

Basic usage. Define director class and pass two different builders of MessageBuilder type.

1
2
3
go run .
{"recepient":"Santa Claus","text":"I was a good body, give me a gift!"}
<XMLMessage><recipient>Santa Claus</recipient><body>I was a good body, give me a gift!</body></XMLMessage>

Sample output.