Chaining de métodos é uma técnica muito utilizada em linguagens como PHP, Java e C#. Se você não está familiarizado com o termo, não se preocupe, pois essa é uma técnica muito simples. Ela consiste em retornar um objeto para que outro método possa ser chamado sem a necessidade de atribuição a uma outra variável.
Essa técnica é muito utilizada em ORMs como o GORM, para construção de queries mais complexas.
No vídeo que postamos no nosso canal do youtube mostrando como construir uma API completa com go-chi e postgres (link para o vídeo), também podemos ver essa técnica sendo utilizada para fazer o decode da request para uma struct.
Para entender melhor seu funcionamento, vamos criar uma struct com 100% de seus atributos privados.
type Pessoa struct { nome string sobrenome string idade uint }
Agora, para que seja possível atribuir valores a esses atributos, vamos implementar um método Set para cada um deles.
func (p *Pessoa) SetNome(nome string) { p.nome = nome } func (p *Pessoa) SetSobrenome(sobrenome string) { p.sobrenome = sobrenome } func (p *Pessoa) SetIdade(idade uint) { p.idade = idade }
Para finalizar, vamos adicionar um método para fazer print das informações em forma de uma frase.
func (p *Pessoa) Print() { fmt.Printf("Olá, meu nome é %s %s e eu tenho %d anos.", p.nome, p.sobrenome, p.idade) }
Da forma como fizemos, para utilizar essa struct, teríamos um código similar a esse.
p := Pessoa{} p.SetNome("Tiago") p.SetSobrenome("Temporin") p.SetIdade(32) p.Print()
Porém, se quisermos chamar os métodos de forma encadeada, ou seja, fazer um chaining de chamadas, precisamos fazer uma pequena mudança nos nossos métodos Set.
Essa mudança consiste basicamente em retornar o ponteiro da struct, ou seja, retornar o p
.
func (p *Pessoa) SetNome(nome string) *Pessoa { p.nome = nome return p } func (p *Pessoa) SetSobrenome(sobrenome string) *Pessoa { p.sobrenome = sobrenome return p } func (p *Pessoa) SetIdade(idade uint) *Pessoa { p.idade = idade return p }
Isso não nos impede de utilizar a struct da mesma forma como já vimos, porém adiciona também a possibilidade de utilizarmos ela da seguinte forma.
p := Pessoa{} p.SetNome("Tiago"). SetSobrenome("Temporin"). SetIdade(32). Print()
Embora essa técnica possa deixar nosso código mais limpo, ela também pode trazer um grande problema em Go, que é o tratamento de erros.
Nas linguagens citadas no inicio, quando um erro ocorre, uma excessão é lançada e a sequencia de chamadas é quebrada, ou seja, a execução do chaining para.
Como não temos excessões em Go, para utilizar a técnica de chaining, os métodos só podem retornar o ponteiro da struct, evitando assim que possamos fazer um tratamento de erro adequado.
Meu conselho? Caso deseje utilizar essa técnica, faça-o somente em métodos simples, locais onde não existe a possibilidade de erro, ou seja, métodos do tipo Set ou preparatórios.
Dessa forma conseguimos utilizar essa técnica e ainda continuar com uma boa qualidade no tratamento de erros.
Deixem suas dúvidas nos comentários.
Até a próxima!