go-sample-webpage/vendor/github.com/lestrrat-go/option/README.md

137 lines
3.1 KiB
Markdown
Raw Normal View History

2021-11-04 01:14:51 +00:00
# option
Base object for what I call the "Optional Parameters Pattern".
The beauty of this pattern is that you can achieve a method that can
take the following simple calling style
```
obj.Method(mandatory1, mandatory2)
```
or the following, if you want to modify its behavior with optional parameters
```
obj.Method(mandatory1, mandatory2, optional1, optional2, optional3)
```
Intead of the more clunky zero value for optionals style
```
obj.Method(mandatory1, mandatory2, nil, "", 0)
```
or the equally cluncky config object style, which requires you to create a
struct with `NamesThatLookReallyLongBecauseItNeedsToIncludeMethodNamesConfig
```
cfg := &ConfigForMethod{
Optional1: ...,
Optional2: ...,
Optional3: ...,
}
obj.Method(mandatory1, mandatory2, &cfg)
```
# SYNOPSIS
This library is intended to be a reusable component to implement
a function with arguments that look like the following:
```
obj.Method(mandatory1, mandatory2, optional1, optional2, optional3, ...)
```
Internally, we just declare this method as follows:
```
func (obj *Object) Method(m1 Type1, m2 Type2, options ...Option) {
...
}
```
Option objects take two arguments, its identifier and the value it contains.
The identifier can be anything, but it's usually better to use a an unexported
empty struct so that only you have the ability to generate said option:
```
type identOptionalParamOne struct{}
type identOptionalParamTwo struct{}
type identOptionalParamThree struct{}
func WithOptionOne(v ...) Option {
return option.New(identOptionalParamOne{}, v)
}
```
Then you can call the method we described above as
```
obj.Method(m1, m2, WithOptionOne(...), WithOptionTwo(...), WithOptionThree(...))
```
Options should be parsed in a code that looks somewhat like this
```
func (obj *Object) Method(m1 Type1, m2 Type2, options ...Option) {
paramOne := defaultValueParamOne
for _, option := range options {
switch option.Ident() {
case identOptionalParamOne{}:
paramOne = option.Value().(...)
}
}
...
}
```
# Simple usage
Most of the times all you need to do is to declare the Option type as an alias
in your code:
```
package myawesomepkg
import "github.com/lestrrat-go/option"
type Option = option.Interface
```
Then you can start definig options like they are described in the SYNOPSIS section.
# Differentiating Options
When you have multiple methods and options, and those options can only be passed to
each one the methods, it's hard to see which options should be passed to which method.
```
func WithX() Option {}
func WithY() Option {}
// Now, which of WithX/WithY go to which method?
func (*Obj) Method1(options ...Option) {}
func (*Obj) Method2(options ...Option) {}
```
In this case the easiest way to make it obvious is to put an extra layer around
the options so that they have different types
```
type Method1Option interface {
Option
method1Option()
}
type method1Option struct { Option }
func (*method1Option) method1Option() {}
func WithX() Method1Option {
return &methodOption{option.New(...)}
}
func (*Obj) Method1(options ...Method1Option) {}
```
This way the compiler knows if an option can be passed to a given method.