CodeBlanks Logo Icon CodeBlanks

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.

Practice 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).

Starter code

main.go
package main

import (
    "fmt"
    "strings"
)

func main() {
    // buraya yaz
}

Sample solution

main.go
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))
}

Expected output

SELECT ad FROM urunler LIMIT 5

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What should a chainable method's return type be?

  • Nothing
  • A pointer to its own type (*Sorgu) Correct answer
  • error
  • string

Explanation: Because each call returns the same object, the next method works on it.

2 Return the receiver

Question
func (s *Sorgu) Limit(n int) *Sorgu {
    s.parcalar = append(s.parcalar, fmt.Sprintf("LIMIT %d", n))
    return ____
}
Answer
func (s *Sorgu) Limit(n int) *Sorgu {
    s.parcalar = append(s.parcalar, fmt.Sprintf("LIMIT %d", n))
    return s
}

Explanation: return s returns the same pointer so the chain can continue.

3 Part and role

Term Meaning
Returning *Sorgu Keeps the chain going
String() Turns the result into text
b.err Stores an error in the chain
(&Sorgu{}) Starting object

All lessons in Beginner