Builder Design Pattern

RMAG news

O padrão de design Builder é utilizado para construir objetos complexos de forma incremental, permitindo a criação de diferentes representações de um objeto utilizando o mesmo processo de construção. Neste artigo, vamos explorar como implementar o padrão Builder em Golang, entender seus benefícios e analisar um exemplo prático de uso.

O que é o Builder?

O padrão Builder separa a construção de um objeto complexo da sua representação, permitindo que o mesmo processo de construção possa criar diferentes representações. Isso é especialmente útil quando um objeto precisa ser criado em várias etapas ou com várias configurações possíveis.

Benefícios do Builder

Separação de Construção e Representação: Permite que a construção de um objeto seja separada da sua representação final.
Construção Incremental: Permite a construção de objetos complexos de maneira incremental e passo a passo.
Reutilização de Código: Facilita a reutilização de código ao definir passos de construção comuns que podem ser combinados de várias maneiras.

Implementando um Builder

Para implementar nosso Builder, vamos imaginar um objeto complexo onde será necessário inicializar vários campos e até outros objetos agrupados. Que tal uma Casa? Onde teremos dois tipos de construção, uma convencional onde será usado concreto e tijolos, e uma segunda de madeira.

1 – Definindo a Estrutura

Primeiro, precisamos definir a estrutura do objeto que queremos construir. Como dito antes, vamos construir uma casa. Dentro dessa Struct colocaremos o que é necessário para criar uma.

// house.go

package main

type House struct {
Foundation string
Structure string
Roof string
Interior string
}

2 – Definindo a Interface do Builder

Ainda no mesmo arquivo, vamos definir a interface do nosso Builder que especifica os métodos necessários para construir as diferentes partes da House.

//house.go

package main

type House struct {
Foundation string
Structure string
Roof string
Interior string
}

type HouseBuilder interface {
SetFoundation()
SetStructure()
SetRoof()
SetInterior()
GetHouse() House
}

3 – Implementando concretamente o Builder

Vamos criar dois arquivos novos, concreteHouse e woodHouse. Eles serão a implementação de uma classe concreta que siga a interface HouseBuilder.

//concreteHouse.go

package main

type ConcreteHouseBuilder struct {
house House
}

func (b *ConcreteHouseBuilder) SetFoundation() {
b.house.Foundation = “Concrete, brick, and stone”
}

func (b *ConcreteHouseBuilder) SetStructure() {
b.house.Structure = “Wood and brick”
}

func (b *ConcreteHouseBuilder) SetRoof() {
b.house.Roof = “Concrete and reinforced steel”
}

func (b *ConcreteHouseBuilder) SetInterior() {
b.house.Interior = “Gypsum board, plywood, and paint”
}

func (b *ConcreteHouseBuilder) GetHouse() House {
return b.house
}

//woodHouse.go

package main

type WoodHouseBuilder struct {
house House
}

func (b *WoodHouseBuilder) SetFoundation() {
b.house.Foundation = “Wooden piles”
}

func (b *WoodHouseBuilder) SetStructure() {
b.house.Structure = “Wooden frame”
}

func (b *WoodHouseBuilder) SetRoof() {
b.house.Roof = “Wooden shingles”
}

func (b *WoodHouseBuilder) SetInterior() {
b.house.Interior = “Wooden panels and paint”
}

func (b *WoodHouseBuilder) GetHouse() House {
return b.house
}

4 – Definindo o Director

O Director é uma classe que gerencia a construção de um objeto, garantindo que as etapas de construção sejam chamadas na ordem correta. Ele não sabe nada sobre os detalhes das implementações específicas do Builder, apenas chama os métodos do Builder em uma sequência lógica para criar o produto final.

//director.go

package main

type Director struct {
builder HouseBuilder
}

func (d *Director) Build() {
d.builder.SetFoundation()
d.builder.SetStructure()
d.builder.SetRoof()
d.builder.SetInterior()
}

func (d *Director) SetBuilder(b HouseBuilder) {
d.builder = b
}

5 – Utilizando o Builder

Finalmente, vamos utilizar o Director e os Builders concretos para construir diferentes tipos de casas.

//main.go

package main

import (
“fmt”
)

func main() {
cb := &builder.ConcreteHouseBuilder{}
director := builder.Director{Builder: cb}

director.Build()
concreteHouse := cb.GetHouse()

fmt.Println(“Concrete House”)
fmt.Println(“Foundation:”, concreteHouse.Foundation)
fmt.Println(“Structure:”, concreteHouse.Structure)
fmt.Println(“Roof:”, concreteHouse.Roof)
fmt.Println(“Interior:”, concreteHouse.Interior)
fmt.Println(“——————————————-“)

wb := &builder.WoodHouseBuilder{}
director.SetBuilder(wb)

director.Build()
woodHouse := wb.GetHouse()

fmt.Println(“Wood House”)
fmt.Println(“Foundation:”, woodHouse.Foundation)
fmt.Println(“Structure:”, woodHouse.Structure)
fmt.Println(“Roof:”, woodHouse.Roof)
fmt.Println(“Interior:”, woodHouse.Interior)
}

Resumindo

Struct House: Representa o produto final que estamos construindo.
Interface HouseBuilder: Define os métodos para construir as diferentes partes da casa.
Implementações Concretas (ConcreteHouseBuilder e WoodHouseBuilder): Implementam a interface HouseBuilder e definem as etapas de construção específicas.

Director: Gerencia o processo de construção, garantindo que as etapas sejam chamadas na ordem correta.
Função main: Demonstra o uso do padrão Builder para construir diferentes tipos de casas, chamando o Director para gerenciar o processo e obtendo o produto final.

Conclusão

O padrão Builder é uma ferramenta para construir objetos complexos de maneira incremental e flexível. Em Golang, a implementação deste padrão é direta e eficaz, permitindo a criação de sistemas modulares e fáceis de manter. Com o uso de interfaces e classes concretas, podemos centralizar a lógica de construção e simplificar a evolução do código à medida que novos requisitos surgem.