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 Builder Pattern separa a construção de um objeto complexo de sua representação. Diferentemente do padrão Functional Options, o Builder usa uma struct construtora dedicada, com métodos que configuram o objeto passo a passo.
Veja 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 é excelente quando os objetos exigem várias etapas de configuração ou quando você quer impor uma sequência específica de construção. Ele é particularmente útil para criar objetos imutáveis ou quando o próprio processo de construção precisa de validação.
Desafio
FácilVamos criar um compositor de mensagens de e-mail usando o Builder Pattern! As mensagens de e-mail têm muitos componentes opcionais, como destinatários, CC, assunto, corpo e anexos, o que as torna candidatas perfeitas para uma 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
Emailcom estes campos:from(string)to(slice de strings)cc(slice de strings)subject(string)body(string)
Crie uma struct
EmailBuilderque contenha um ponteiro para um Email em construção. ImplementeNewEmailBuilder(), que retorna um novo builder com um Email inicializado (slices vazios paratoecc).Adicione estes métodos do builder, cada um retornando
*EmailBuilderpara permitir o encadeamento:From(address string)- define o remetenteTo(address string)- adiciona um destinatário ao slicetoCC(address string)- adiciona um destinatário ao sliceccSubject(subject string)- define a linha de assuntoBody(body string)- define o corpo do e-mail
Adicione um método
Build()que retorne o*Emailconstruído.Por fim, adicione um método
Summary()em Email que retorne 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 a quantidade de destinatários e cada endereço de destinatário. Em seguida, leia a quantidade de destinatários em CC e cada endereço de CC. Por fim, leia o assunto e o corpo.
Use o EmailBuilder com encadeamento de métodos para construir o e-mail e, depois, imprima o resumo.
As entradas a seguir 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 em CC
- Linhas seguintes: Cada endereço de CC
- Próxima linha: Assunto
- Linha final: Corpo
Por exemplo, considerando:
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 considerando:
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, por meio de chamadas repetidas de To(), e como o padrão mantém o processo de construção limpo e legível, mesmo com muitos campos opcionais.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler endereço do remetente
from, _ := reader.ReadString('\n')
from = strings.TrimSpace(from)
// Ler 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 número de destinatários em CC
ccCountStr, _ := reader.ReadString('\n')
ccCount, _ := strconv.Atoi(strings.TrimSpace(ccCountStr))
// Ler cada endereço de CC
ccAddresses := make([]string, ccCount)
for i := 0; i < ccCount; i++ {
addr, _ := reader.ReadString('\n')
ccAddresses[i] = strings.TrimSpace(addr)
}
// Ler assunto
subject, _ := reader.ReadString('\n')
subject = strings.TrimSpace(subject)
// Ler corpo
body, _ := reader.ReadString('\n')
body = strings.TrimSpace(body)
// TODO: Use o EmailBuilder com encadeamento de métodos para construir o email
// Comece com NewEmailBuilder(), depois encadeie From(), To(), CC(), Subject(), Body()
// Finalmente chame Build() para obter o Email
// TODO: Imprima o resumo do email usando o método Summary()
}
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
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker Pool12Conceitos Avançados de OOP
Padrão de Opções FuncionaisPadrão Builder em GoEncadeamento de MétodosAliases de Tipos vs. DefiniçõesNoções Básicas de ReflectionVisão Geral da Geração de CódigoPratique por conta própria: Compilador de Go online