Builder Pattern You Should Know in Golang(Design Patterns 01)

Building Complex Objects with Ease: An Introduction to the Builder Pattern in Go

Contact Author@:Medium,LinkedIn,Twitter

golang-builder-pattern(from github.com/MariaLetta/free-gophers-pack)|300

Hello, here is Wesley, Today’s article is about builder pattern in Go. Without further ado, let’s get started.💪

In golang,design patterns are a way to implement software engineering best practices. Among them, Builder Pattern is a creational design pattern that creates complex objects step by step, while avoiding situations where constructors have too many parameters.

Core Concepts of the Builder Pattern

  1. Intent: Separate the construction process of complex objects from their representation, allowing for different representations to be created using the same construction process.
  2. Applicability:
    • When creating complex objects requires a step-by-step approach.
    • When constructors have too many parameters or parameter combinations make object creation complicated.
  3. Advantages:
    • Separate object creation and representation, conforming to the single responsibility principle.
    • More flexible and easier to extend.
  4. Disadvantages:
    • Increase class complexity.
    • May be slightly more cumbersome for creating simple objects.

Structure of the Builder Pattern

The Builder pattern primarily consists of the following components:

  1. Product: The complex object to be created.
  2. Builder: Defines the interface for creating objects, including all construction steps.
  3. ConcreteBuilder: Concretely implements the Builder interface, completing each part of the construction process.
  4. Director: Guides the construction process, defining the order in which the object is created.

Implementation in Go

Here’s an example of implementing the Builder pattern in Go, using a computer assembly as an example:

1. Define the product structure

1
2
3
4
5
6
7
8
// Computer structure, describing the important components of a computer
type Computer struct {
CPU string
Memory string
Mainboard string
HardDisk string
GPU string
}

2. Define the Builder interface

1
2
3
4
5
6
7
8
9
// Builder interface, defining methods for generating instances
type ComputerBuilder interface {
SetCPU() ComputerBuilder
SetMemory() ComputerBuilder
SetMainboard() ComputerBuilder
SetHardDisk() ComputerBuilder
SetGPU() ComputerBuilder
Build() Computer
}

3. Implementing Concrete Builders

Office Model Builder

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
type OfficeModelBuilder struct {  
computer Computer
}

func NewOfficeModelBuilder() *OfficeModelBuilder {
return &OfficeModelBuilder{}
}

func (ob *OfficeModelBuilder) SetCPU() ComputerBuilder {
ob.computer.CPU = "Intel-i3"
return ob
}

func (ob *OfficeModelBuilder) SetMemory() ComputerBuilder {
ob.computer.Memory = "8G"
return ob
}

func (ob *OfficeModelBuilder) SetMainboard() ComputerBuilder {
ob.computer.Mainboard = "B460"
return ob
}

func (ob *OfficeModelBuilder) SetHardDisk() ComputerBuilder {
ob.computer.HardDisk = "256G HDD"
return ob
}

func (ob *OfficeModelBuilder) SetGPU() ComputerBuilder {
ob.computer.GPU = "Intel HD630"
return ob
}

func (ob *OfficeModelBuilder) Build() Computer {
return ob.computer
}

Game Model Builder

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
type GameModelBuilder struct {  
computer Computer
}

func NewGameModelBuilder() *GameModelBuilder {
return &GameModelBuilder{}
}

func (gb *GameModelBuilder) SetCPU() ComputerBuilder {
gb.computer.CPU = "Intel-i9"
return gb
}

func (gb *GameModelBuilder) SetMemory() ComputerBuilder {
gb.computer.Memory = "32G"
return gb
}

func (gb *GameModelBuilder) SetMainboard() ComputerBuilder {
gb.computer.Mainboard = "X460"
return gb
}

func (gb *GameModelBuilder) SetHardDisk() ComputerBuilder {
gb.computer.HardDisk = "512G SSD, 1T HDD"
return gb
}

func (gb *GameModelBuilder) SetGPU() ComputerBuilder {
gb.computer.GPU = "NVIDIA GTX3090"
return gb
}

func (gb *GameModelBuilder) Build() Computer {
return gb.computer
}

4. Director Implementation

1
2
3
4
5
6
7
8
9
10
type Director struct{}  

func (d *Director) Construct(builder ComputerBuilder) Computer {
return builder.SetCPU().
SetMemory().
SetMainboard().
SetHardDisk().
SetGPU().
Build()
}

5. Test Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
director := &Director{}

officeBuilder := NewOfficeModelBuilder()
officeComputer := director.Construct(officeBuilder)
fmt.Printf("Office-type host configuration:%v\n", officeComputer)

gameBuilder := NewGameModelBuilder()
gameComputer := director.Construct(gameBuilder)
fmt.Printf("Game-type host configuration:%v\n", gameComputer)
}
// output
Office-type host configuration:{Intel-i3 8G B460 256G HDD Intel HD630}
Game-type host configuration:{Intel-i9 32G X460 512G SSD, 1T HDD NVIDIA GTX3090}

6. Thoughts on implementing Go’s builder pattern

The implementation above references Java’s implementation method, but Go should have its own characteristics. Go Is Not Java

  1. Removing Director:
    In Go’s implementation, Director is not necessary. Placing the construction logic directly in the builder function (OfficeComputerBuilder and GameComputerBuilder) is more in line with Go’s simple and direct style.
  2. Method chaining:
    The GenericBuilder uses method chaining (methods return themselves), making it easy to build objects step by step.
  3. Clearer responsibility division:
    GenericBuilder is a general builder, while specific builders like OfficeComputerBuilder and GameComputerBuilder are responsible for generating specific computer configurations.
  4. Avoiding over-design:
    Go advocates simplicity, so there’s no need to force the use of complex design patterns like Java.

You can also refer to the article: Design patterns in Golang — The Builder | by Surya Reddy | Medium

Summary

  1. The Builder pattern is typically used in scenarios where there are multiple constructor parameters or a large number of build steps involved. Using the Builder pattern can simplify the number of constructor parameters, making the building process more organized.
  2. It is possible to provide multiple different implementations for the same product. For example, in the above code, two different implementations were created for the Computer type: an office computer built through NewOfficeModelBuilder, and a game computer built through NewGameModelBuilder.
  3. It can be applied to scenarios where the building process is not allowed to be interrupted. Still referring to the previous code, the Computer type object either completes or does not exist at all, without intermediate states, because the struct director encapsulates the corresponding process, and intermediate states only exist in ConcreteBuilder.

More Series Articles about You Should Know In Golang:

https://wesley-wei.medium.com/list/you-should-know-in-golang-e9491363cd9a

And I’m Wesley, delighted to share knowledge from the world of programming. 

Don’t forget to follow me for more informative content, or feel free to share this with others who may also find it beneficial. it would be a great help to me.

Give me some free applauds, highlights, or replies, and I’ll pay attention to those reactions, which will determine whether or not I continue to post this type of article.

See you in the next article. 👋

中文文章: https://programmerscareer.com/zh-cn/golang-builder-pattern/
Author: Medium,LinkedIn,Twitter
Note: Originally written at https://programmerscareer.com/golang-builder-pattern/ at 2024-11-19 00:35.
Copyright: BY-NC-ND 3.0

Factory Pattern You Should Know in Golang(Design Patterns 02) Enlightening Weekly 001: Understanding Code and Investment Insights

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×