Como fazer encadeamento de métodos (chaining)

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!


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Deixe uma resposta