Skip to content

Latest commit

 

History

History
260 lines (187 loc) · 7.35 KB

README.ja.md

File metadata and controls

260 lines (187 loc) · 7.35 KB

foggo

ci Release Coverage Status

Golang の構造体から Functional Option Pattern コードを自動生成する cli

Installation

$ go install golang.org/x/tools/cmd/goimports@latest  # foggo use 'goimports' command
$ go install github.com/s14t284/foggo@latest

Usage

foggo では fopafop サブコマンドを提供しています。

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)

Generate with command line

  1. 以下のように構造体を用意します。optionを提供したくないフィールドには構造体タグに foggo:"-" を付与してください。

    // ./image/image.go
    package image
    
    type Image struct {
        Width  int
        Height int
        Src    string `foggo:"-"`
        Alt    string
    }
  2. foggo fop コマンドを実行。

    # struct パラメータには構造体名を指定します
    # package パラメータには構造体が配置されている相対パスを指定します
    $ foggo fop --struct Image --package image
  3. 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
        }
    }
  4. あとは 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"
        ...
    }

Generate with go:generate

  1. 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
    }
  2. go generate ./... コマンドを実行してください。

    $ go generate ./...
  3. go:generate foggo コメントが付与されている全ての構造体に対して Functional Option Pattern のコードが自動生成されます。

Generate with afop command

afopApplicable Functional Option Pattern のコードを自動生成するコマンドです。

  1. 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
    }
  2. go generate ./... コマンドを実行してください。

    $ go generate ./...
  3. 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
    }
  4. あとは 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 ?

Functional Option Pattern(FOP) は Golang でよく使われるデザインパターンの一種です。

Golang では python や ruby で利用できるキーワード引数のようなオプション引数を提供していません。 FOP を使うことで、オプション引数を再現します。

以下の記事が詳しいです。

Applicable Functional Option Pattern ?

Applicable Functional Option Pattern(AFOP) は生成されるオプションがテスト可能な FOP です。 FOP ではオプションを関数として定義します。 そのため、同一引数を持つオプション関数同士を比較しても等しくないと判定されてしまいます。

AFOP ではオプションを 単一のパラメータを持ち、apply メソッドを実装した構造体として定義します。 Go言語では構造体は比較可能であるため、同一引数を持つオプション同士を比較することができます。(すなわちテスト可能です)

AFOP については以下の記事が詳しいです。

References