Go
Method chaining
Learn to return the receiver for method chaining.
Method chaining
If a method returns the receiver (as a pointer), calls can be written one after another; this is called a fluent API:
type Sorgu struct{ parcalar []string }
func (s *Sorgu) Sec(a string) *Sorgu { s.parcalar = append(s.parcalar, "SELECT "+a); return s }
func (s *Sorgu) Tablo(t string) *Sorgu { s.parcalar = append(s.parcalar, "FROM "+t); return s }
func (s *Sorgu) String() string { return strings.Join(s.parcalar, " ") }
q := (&Sorgu{}).Sec("ad").Tablo("urunler")
fmt.Println(q) // SELECT ad FROM urunler
Chaining with errors
Chaining isn't common in Go because it is hard to return an error in the middle of a chain. One solution is to store the error inside the object and check it at the end:
type Builder struct {
err error
// ...
}
func (b *Builder) Adim(x int) *Builder {
if b.err != nil {
return b // skip if there was an earlier error
}
if x < 0 {
b.err = errors.New("negatif değer")
}
return b
}
bufio.Scanner's Err() method uses a similar idea.
Exercise Query builder
Write the Sorgu type above with the Sec, Tablo and String methods, and also add Limit(n int) *Sorgu. Print the result of (&Sorgu{}).Sec("ad").Tablo("urunler").Limit(5).
Show example solution
package main
import (
"fmt"
"strings"
)
type Sorgu struct{ parcalar []string }
func (s *Sorgu) Sec(a string) *Sorgu {
s.parcalar = append(s.parcalar, "SELECT "+a)
return s
}
func (s *Sorgu) Tablo(t string) *Sorgu {
s.parcalar = append(s.parcalar, "FROM "+t)
return s
}
func (s *Sorgu) Limit(n int) *Sorgu {
s.parcalar = append(s.parcalar, fmt.Sprintf("LIMIT %d", n))
return s
}
func (s *Sorgu) String() string { return strings.Join(s.parcalar, " ") }
func main() {
fmt.Println((&Sorgu{}).Sec("ad").Tablo("urunler").Limit(5))
}
Test yourself
-
What should a chainable method's return type be?
- Nothing
- A pointer to its own type (*Sorgu)
- error
- string
Show answer
A pointer to its own type (*Sorgu) — Because each call returns the same object, the next method works on it.
-
Return the receiver
func (s *Sorgu) Limit(n int) *Sorgu {
s.parcalar = append(s.parcalar, fmt.Sprintf("LIMIT %d", n))
return ____
}
Show answer
func (s *Sorgu) Limit(n int) *Sorgu {
s.parcalar = append(s.parcalar, fmt.Sprintf("LIMIT %d", n))
return s
}
-
Part and role
- Returning *Sorgu
- Keeps the chain going
- String()
- Turns the result into text
- b.err
- Stores an error in the chain
- (&Sorgu{})
- Starting object
Next lesson: Summary - Methods