Encadeamento de Métodos
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 81 de 107.
Encadeamento de métodos é uma técnica em que os métodos retornam o objeto receptor, permitindo que várias chamadas de método sejam encadeadas em uma única instrução. Você já viu isso no padrão Builder, mas é um conceito independente, útil em muitos contextos.
A chave é retornar um ponteiro para a struct de cada método:
type Query struct {
table string
columns []string
where string
limit int
}
func (q *Query) Select(cols ...string) *Query {
q.columns = cols
return q
}
func (q *Query) From(table string) *Query {
q.table = table
return q
}
func (q *Query) Where(condition string) *Query {
q.where = condition
return q
}
func (q *Query) Limit(n int) *Query {
q.limit = n
return q
}Agora você pode encadear essas chamadas de forma fluente:
query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)Isso quase parece uma linguagem natural em comparação com instruções separadas. O encadeamento de métodos funciona melhor quando as operações configuram ou modificam um objeto sem precisar retornar outros valores. Ele é comumente usado em construtores de consultas, APIs de configuração e bibliotecas de asserções para testes.
Lembre-se de que cada método deve usar um receptor de ponteiro e retornar *StructType para que a cadeia funcione corretamente.
Desafio
FácilVamos criar um construtor fluente de elementos HTML que usa encadeamento de métodos para construir tags HTML com atributos e conteúdo! Esta é uma aplicação prática do encadeamento de métodos: você criará uma API que se lê quase como linguagem natural ao construir elementos HTML.
Você organizará seu código em dois arquivos:
element.go: Defina seu construtor de elementos HTML com métodos encadeáveis.Crie uma struct
Elementque armazene o nome da tag, um mapa de atributos (de string para string) e o conteúdo interno. Crie estes métodos encadeáveis, cada um retornando*Element:NewElement(tag string)- construtor que cria um novo Element com o nome de tag fornecido e um mapa de atributos inicializado (vazio)Attr(key, value string)- adiciona um atributo ao elementoID(id string)- método de conveniência que define o atributo "id"Class(class string)- método de conveniência que define o atributo "class"Content(text string)- define o conteúdo interno do elemento
Adicione um método
Render()que retorne a string HTML. Os atributos devem aparecer em ordem alfabética por chave. O formato é:<tag attr1="value1" attr2="value2">content</tag>Se não houver atributos, simplesmente renderize
<tag>content</tag>. Se não houver conteúdo, renderize<tag attr="value"></tag>.main.go: Construa elementos HTML usando encadeamento de métodos.Leia o nome da tag e, em seguida, leia uma contagem de operações a serem realizadas. Cada operação é uma destas:
idseguido pelo valor do IDclassseguido pelo valor da classeattrseguido pelo nome e pelo valor do atributo (duas linhas)contentseguido pelo texto do conteúdo
Encadeie todas as operações de forma fluente e, então, imprima o HTML renderizado.
As seguintes entradas serão fornecidas:
- Linha 1: Nome da tag
- Linha 2: Número de operações
- Linhas seguintes: Tipo da operação e valor(es)
Por exemplo, dada a entrada:
div
3
id
container
class
main-content
content
Hello WorldSua saída deve ser:
<div class="main-content" id="container">Hello World</div>E dada a entrada:
a
3
attr
href
https://example.com
attr
target
_blank
content
Click hereSua saída deve ser:
<a href="https://example.com" target="_blank">Click here</a>E dada a entrada:
input
2
attr
type
text
attr
placeholder
Enter nameSua saída deve ser:
<input placeholder="Enter name" type="text"></input>E dada a entrada:
p
1
content
Simple paragraphSua saída deve ser:
<p>Simple paragraph</p>Observe como o encadeamento de métodos permite construir elementos passo a passo de maneira legível. Cada método modifica o elemento e o retorna, permitindo que a próxima chamada de método seja encadeada diretamente.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Ler o nome da tag
scanner.Scan()
tagName := scanner.Text()
// Ler o número de operações
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// Criar o elemento
element := NewElement(tagName)
// Processar cada operação
for i := 0; i < numOps; i++ {
scanner.Scan()
operation := scanner.Text()
// TODO: Com base no tipo da operação, encadeie o método apropriado
// As operações podem ser: "id", "class", "attr" ou "content"
// - Para "id": leia a próxima linha como o valor do ID
// - Para "class": leia a próxima linha como o valor da class
// - Para "attr": leia as próximas DUAS linhas (nome do atributo, depois o valor)
// - Para "content": leia a próxima linha como o texto do conteúdo
// Use o encadeamento de métodos para construir o elemento de forma fluente
_ = operation // Remova esta linha ao implementar
}
// Imprime o HTML renderizado
fmt.Println(element.Render())
}
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