option

package module
Version: v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 25, 2021 License: MIT Imports: 0 Imported by: 12

README

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.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Interface

type Interface interface {
	// Ident returns the "indentity" of this option, a unique identifier that
	// can be used to differentiate between options
	Ident() interface{}

	// Value returns the corresponding value.
	Value() interface{}
}

Interface defines the minimum interface that an option must fulfill

func New

func New(ident, value interface{}) Interface

New creates a new Option

Source Files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL