Menu
Coddy logo textTech

Padrão Builder em Go

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 80 de 107.

O Padrão Builder separa a construção de um objeto complexo de sua representação. Ao contrário do padrão Functional Options, o Builder utiliza uma struct builder dedicada com métodos que configuram o objeto passo a passo.

Aqui está como implementar um builder para uma requisição HTTP:

type Request struct {
    method  string
    url     string
    headers map[string]string
    body    string
}

type RequestBuilder struct {
    request *Request
}

func NewRequestBuilder() *RequestBuilder {
    return &RequestBuilder{
        request: &Request{
            headers: make(map[string]string),
        },
    }
}

func (b *RequestBuilder) Method(m string) *RequestBuilder {
    b.request.method = m
    return b
}

func (b *RequestBuilder) URL(u string) *RequestBuilder {
    b.request.url = u
    return b
}

func (b *RequestBuilder) Header(key, value string) *RequestBuilder {
    b.request.headers[key] = value
    return b
}

func (b *RequestBuilder) Build() *Request {
    return b.request
}

Cada método retorna o ponteiro do builder, permitindo o encadeamento de métodos. O método Build() finaliza e retorna o objeto construído:

req := NewRequestBuilder().
    Method("POST").
    URL("https://api.example.com").
    Header("Content-Type", "application/json").
    Build()

O padrão Builder se destaca quando os objetos exigem várias etapas de configuração ou quando você deseja impor uma sequência de construção específica. Ele é particularmente útil para criar objetos imutáveis ou quando o próprio processo de construção precisa de validação.

challenge icon

Desafio

Fácil

Vamos construir um compositor de mensagens de e-mail usando o Builder Pattern! Mensagens de e-mail têm muitos componentes opcionais — destinatários, CC, assunto, corpo, anexos — tornando-as candidatas perfeitas para a construção passo a passo com encadeamento de métodos.

Você organizará seu código em dois arquivos:

  • email.go: Defina sua estrutura de e-mail e seu builder.

    Crie uma struct Email com estes campos:

    • from (string)
    • to (slice de strings)
    • cc (slice de strings)
    • subject (string)
    • body (string)

    Crie uma struct EmailBuilder que contém um ponteiro para um Email sendo construído. Implemente NewEmailBuilder() que retorna um novo builder com um Email inicializado (slices vazios para to e cc).

    Adicione estes métodos ao builder, cada um retornando *EmailBuilder para encadeamento:

    • From(address string) - define o remetente
    • To(address string) - adiciona um destinatário ao slice to
    • CC(address string) - adiciona um destinatário ao slice cc
    • Subject(subject string) - define a linha de assunto
    • Body(body string) - define o corpo do e-mail

    Adicione um método Build() que retorna o *Email construído.

    Finalmente, adicione um método Summary() no Email que retorna uma string neste formato:

    From: [from]
    To: [comma-separated to addresses]
    CC: [comma-separated cc addresses or "none"]
    Subject: [subject]
    Body: [body]
  • main.go: Construa e exiba mensagens de e-mail usando o builder.

    Leia o endereço do remetente, depois leia uma contagem de destinatários e cada endereço de destinatário. Em seguida, leia uma contagem de destinatários CC e cada endereço CC. Finalmente, leia o assunto e o corpo.

    Use o EmailBuilder com encadeamento de métodos para construir o e-mail e, em seguida, imprima seu resumo.

As seguintes entradas serão fornecidas:

  • Linha 1: Endereço do remetente
  • Linha 2: Número de destinatários
  • Linhas seguintes: Cada endereço de destinatário
  • Próxima linha: Número de destinatários CC
  • Linhas seguintes: Cada endereço CC
  • Próxima linha: Assunto
  • Linha final: Corpo

Por exemplo, dado:

alice@company.com
2
bob@company.com
carol@company.com
1
manager@company.com
Weekly Report
Please find the weekly report attached.

Sua saída deve ser:

From: alice@company.com
To: bob@company.com, carol@company.com
CC: manager@company.com
Subject: Weekly Report
Body: Please find the weekly report attached.

E dado:

support@service.com
1
customer@email.com
0
Your Ticket Update
Your support ticket has been resolved.

Sua saída deve ser:

From: support@service.com
To: customer@email.com
CC: none
Subject: Your Ticket Update
Body: Your support ticket has been resolved.

Observe como o builder permite adicionar vários destinatários um de cada vez através de chamadas repetidas a To(), e como o padrão mantém o processo de construção limpo e legível, mesmo com muitos campos opcionais.

Folha de consulta

O Builder Pattern separa a construção de um objeto complexo de sua representação usando uma struct builder dedicada com métodos que configuram o objeto passo a passo.

Estrutura básica:

type Request struct {
    method  string
    url     string
    headers map[string]string
    body    string
}

type RequestBuilder struct {
    request *Request
}

func NewRequestBuilder() *RequestBuilder {
    return &RequestBuilder{
        request: &Request{
            headers: make(map[string]string),
        },
    }
}

Os métodos do builder retornam o ponteiro do builder para permitir o encadeamento de métodos:

func (b *RequestBuilder) Method(m string) *RequestBuilder {
    b.request.method = m
    return b
}

func (b *RequestBuilder) URL(u string) *RequestBuilder {
    b.request.url = u
    return b
}

func (b *RequestBuilder) Header(key, value string) *RequestBuilder {
    b.request.headers[key] = value
    return b
}

O método Build() finaliza e retorna o objeto construído:

func (b *RequestBuilder) Build() *Request {
    return b.request
}

Uso com encadeamento de métodos:

req := NewRequestBuilder().
    Method("POST").
    URL("https://api.example.com").
    Header("Content-Type", "application/json").
    Build()

O padrão Builder é útil quando objetos exigem várias etapas de configuração, ao impor uma sequência de construção específica, para criar objetos imutáveis ou quando o processo de construção precisa de validação.

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Ler o endereço do remetente
	from, _ := reader.ReadString('\n')
	from = strings.TrimSpace(from)

	// Ler o número de destinatários
	toCountStr, _ := reader.ReadString('\n')
	toCount, _ := strconv.Atoi(strings.TrimSpace(toCountStr))

	// Ler cada endereço de destinatário
	toAddresses := make([]string, toCount)
	for i := 0; i < toCount; i++ {
		addr, _ := reader.ReadString('\n')
		toAddresses[i] = strings.TrimSpace(addr)
	}

	// Ler o número de destinatários CC
	ccCountStr, _ := reader.ReadString('\n')
	ccCount, _ := strconv.Atoi(strings.TrimSpace(ccCountStr))

	// Ler cada endereço CC
	ccAddresses := make([]string, ccCount)
	for i := 0; i < ccCount; i++ {
		addr, _ := reader.ReadString('\n')
		ccAddresses[i] = strings.TrimSpace(addr)
	}

	// Ler o assunto
	subject, _ := reader.ReadString('\n')
	subject = strings.TrimSpace(subject)

	// Ler o corpo
	body, _ := reader.ReadString('\n')
	body = strings.TrimSpace(body)

	// TODO: Use o EmailBuilder com encadeamento de métodos para construir o e-mail
	// Comece com NewEmailBuilder(), depois encadeie From(), To(), CC(), Subject(), Body()
	// Finalmente, chame Build() para obter o Email

	// TODO: Imprima o resumo do e-mail usando o método Summary()
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos