Golang の構造体から Functional Option Pattern
コードを自動生成する cli
$ go install golang.org/x/tools/cmd/goimports@latest # foggo use 'goimports' command
$ go install github.com/s14t284/foggo@latest
foggo では fop
と afop
サブコマンドを提供しています。
Usage:
foggo (fop|afop) [flags]
Flags:
-h, --help help for fop
Global Flags:
-p, --package string Package name having target struct (default ".")
-s, --struct string Target struct name (required)
-
以下のように構造体を用意します。optionを提供したくないフィールドには構造体タグに
foggo:"-"
を付与してください。// ./image/image.go package image type Image struct { Width int Height int Src string `foggo:"-"` Alt string }
-
foggo fop
コマンドを実行。# struct パラメータには構造体名を指定します # package パラメータには構造体が配置されている相対パスを指定します $ foggo fop --struct Image --package image
-
foggo
コマンドにより、以下のような Functional Option Pattern のコードが./image/image_gen.go
に自動生成されます。// Code generated by foggo; DO NOT EDIT. package image type ImageOption func(*Image) func NewImage(options ...ImageOption) *Image { s := &Image{} for _, option := range options { option(s) } return s } func WithWidth(Width int) ImageOption { return func(args *Image) { args.Width = Width } } func WithHeight(Height int) ImageOption { return func(args *Image) { args.Height = Height } } func WithAlt(Alt string) ImageOption { return func(args *Image) { args.Alt = Alt } }
-
あとは
Functional Option Pattern
を使って実装するだけです。package main import "github.com/user/project/image" func main() { image := NewImage( WithWidth(1280), WithHeight(720), WithAlt("alt title"), ) image.Src = "./image.png" ... }
-
go:generate
コメントを付与して構造体を定義します。packageパラメータには何も指定しなくて大丈夫です。// ./image/image.go package image //go:generate foggo fop --struct Image type Image struct { Width int Height int // don't want to create option, specify `foggo:"-"` as the structure tag Src string `foggo:"-"` Alt string }
-
go generate ./...
コマンドを実行してください。$ go generate ./...
-
go:generate foggo
コメントが付与されている全ての構造体に対してFunctional Option Pattern
のコードが自動生成されます。
afop
は Applicable Functional Option Pattern
のコードを自動生成するコマンドです。
-
go:generate
コメントを付与して構造体を定義します。サブコマンドとしてafop
コマンドを指定します// ./image/image.go package image //go:generate foggo afop --struct Image type Image struct { Width int Height int // don't want to create option, specify `foggo:"-"` as the structure tag Src string `foggo:"-"` Alt string }
-
go generate ./...
コマンドを実行してください。$ go generate ./...
-
go:generate foggo
コメントが付与されている全ての構造体に対してApplicable Functional Option Pattern
のコードが自動生成されます。// Code generated by foggo; DO NOT EDIT. package image type ImageOption interface { apply(*Image) } type WidthOption struct { Width int } func (o WidthOption) apply(s *Image) { s.Width = o.Width } type HeightOption struct { Height int } func (o HeightOption) apply(s *Image) { s.Height = o.Height } type AltOption struct { Alt string } func (o AltOption) apply(s *Image) { s.Alt = o.Alt } func NewImage(options ...ImageOption) *Image { s := &Image{} for _, option := range options { option.apply(s) } return s }
-
あとは
Applicable Functional Option Pattern
を使って実装するだけです。package main import "github.com/user/project/image" func main() { image := NewImage( WidthOption(1280), HeightOption(720), AltOption("alt title"), ) image.Src = "./image.png" ... }
Functional Option Pattern
(FOP
) は Golang でよく使われるデザインパターンの一種です。
Golang では python や ruby で利用できるキーワード引数のようなオプション引数を提供していません。
FOP
を使うことで、オプション引数を再現します。
以下の記事が詳しいです。
- Goでオプショナルパラメータをどう扱うか
- Functional Options Pattern に次ぐ、オプション引数を実現する方法
- https://commandcenter.blogspot.jp/2014/01/self-referential-functions-and-design.html
- https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
Applicable Functional Option Pattern
(AFOP
) は生成されるオプションがテスト可能な FOP
です。
FOP
ではオプションを関数として定義します。
そのため、同一引数を持つオプション関数同士を比較しても等しくないと判定されてしまいます。
AFOP
ではオプションを 単一のパラメータを持ち、apply
メソッドを実装した構造体として定義します。
Go言語では構造体は比較可能であるため、同一引数を持つオプション同士を比較することができます。(すなわちテスト可能です)
AFOP
については以下の記事が詳しいです。
- Functional Options Pattern に次ぐ、オプション引数を実現する方法
Applicable Functional Option Pattern
はこの記事で命名されているため名称を拝借しました
- https://github.com/uber-go/guide/blob/master/style.md#functional-options