Problem
You have created a super performant csv reader that look like:
package awesomecsvreader
type csvReader struct{
filename string
delim string
escape string
}
func (*csvReader) Read() {
//...your awesome secret sauce
}
But whats missing it a simple API for users to use your module, that doesn;t look like this:
func NewCsvReader(filename string, delim string, escape string) {
....
}
// ? what happens when new options are added like "separator".
// red alert: api could break!
Solution
Thoughts:
One could use structs for options but that has the following demerits:
- there is no good way of setting up default values for the options, unless the defaults are equal to zero values
- an empty options struct needs to be passed by the user of the api
So, instead of using struct, use the power of functions with closures
Let's code
Commented inline for brevity.
// your module
package awesomecsvreader
import (
"fmt"
)
// this is what my csvReader struct looks like
type csvReader struct{
filename string
delim string
escape string
}
// I define a type that is a func that takes csvReader and sets the option
type option func(*csvReader)
// Take the delim string and return the option
func SetDelim(delim string) option{
return func(cr *csvReader) {
(*cr).delim = delim
}
}
// Take the escape string and return the option
func SetEscape(escape string) option{
return func(cr *csvReader) {
cr.escape = escape
}
}
// Constructor that takes required values like filename
// and setters that are of type option
func NewCsvReader(filename string, setters ...option) *csvReader{
// construct a csvReader with default values
cr := csvReader{
filename: filename,
delim: "CRLB",
escape: "ESC",
}
// change the default values as per those supplied by the user
// by calling the option
for _, setter := range setters {
setter(&cr)
}
return &cr
}
// I am a user of your API
package main
func main() {
//
// create a csvReader with default options
fmt.Println(NewCsvReader("file123"))
//
// create a csvReader with mix of default and custom options
r1 := NewCsvReader("file123", SetDelim("XXX"))
fmt.Println(r1)
//
// create a csvReader with all custom options
r2 := NewCsvReader("file123", SetDelim("XXX"), SetEscape("YYY"))
fmt.Println(r2)
}
// output
&{file123 CRLB ESC}
&{file123 XXX ESC}
&{file123 XXX YYY}
Good ref: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
Top comments (0)