Skip to content

Commit

Permalink
Merge pull request #11 from s14t284/feature/generate-afoc
Browse files Browse the repository at this point in the history
Feature afoc command
  • Loading branch information
rikeda71 authored Jan 15, 2022
2 parents 7402e12 + 637f64b commit 2e7ddcc
Show file tree
Hide file tree
Showing 12 changed files with 524 additions and 58 deletions.
127 changes: 116 additions & 11 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ $ go install github.com/s14t284/foggo@latest

## Usage

__foggo__ では `foc` サブコマンドを提供しています。
https://ww24.jp/2019/07/go-option-pattern で提案されているような mock を用いたテストでも利用しやすいオプションを自動生成する `afoc` サブコマンドも提供予定です。
__foggo__ では `fop``afop` サブコマンドを提供しています。

```shell
Usage:
foggo foc [flags]
foggo (fop|afop) [flags]

Flags:
-h, --help help for foc
-h, --help help for fop

Global Flags:
-p, --package string Package name having target struct (default ".")
Expand All @@ -47,12 +46,12 @@ Global Flags:
}
```

2. `foggo foc` コマンドを実行。
2. `foggo fop` コマンドを実行。

```shell
# struct パラメータには構造体名を指定します
# package パラメータには構造体が配置されている相対パスを指定します
$ foggo foc --struct Image --package image
$ foggo fop --struct Image --package image
```

3. `foggo` コマンドにより、以下のような Functional Option Pattern のコードが `./image/image_gen.go` に自動生成されます。
Expand Down Expand Up @@ -93,7 +92,7 @@ Global Flags:
}
```

4. あとは`Functional Option Pattern` を使って実装するだけです。
4. あとは `Functional Option Pattern` を使って実装するだけです。

```go
package main
Expand All @@ -119,7 +118,7 @@ Global Flags:
// ./image/image.go
package image
//go:generate foggo foc --struct Image
//go:generate foggo fop --struct Image
type Image struct {
Width int
Height int
Expand All @@ -135,13 +134,104 @@ Global Flags:
$ go generate ./...
```

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

### Generate with `afop` command

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

1. `go:generate` コメントを付与して構造体を定義します。サブコマンドとして `afop` コマンドを指定します

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

```shell
$ go generate ./...
```

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

```go
// 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` を使って実装するだけです。

```go
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`Golang でよく使われるデザインパターンの一種です。
`Functional Option Pattern`(`FOP`)Golang でよく使われるデザインパターンの一種です。

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

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

Expand All @@ -150,6 +240,21 @@ Golang では python や ruby で利用できるキーワード引数のよう
- 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 ?

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

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

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

- [Functional Options Pattern に次ぐ、オプション引数を実現する方法](https://ww24.jp/2019/07/go-option-pattern)
- `Applicable Functional Option Pattern` はこの記事で命名されているため名称を拝借しました
- https://github.com/uber-go/guide/blob/master/style.md#functional-options

## References

- https://github.com/moznion/gonstructor
123 changes: 114 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

[日本語版 README](./README.ja.md)

__foggo__ is generator of `Functional Option Pattern` from struct field in Golang code.
__foggo__ is generator of `Functional Option Pattern` And `Applicable Functional Option Pattern` from struct field in Golang code.

## Installation

Expand All @@ -17,14 +17,14 @@ $ go install github.com/s14t284/foggo@latest

## Usage

__foggo__ provides `foc` subcommand. (`afoc` subcommand will be provided in the future)
__foggo__ provides `fop` and `afop` subcommand.

```shell
Usage:
foggo foc [flags]
foggo (fop|afop) [flags]

Flags:
-h, --help help for foc
-h, --help help for fop

Global Flags:
-p, --package string Package name having target struct (default ".")
Expand All @@ -48,12 +48,12 @@ Global Flags:
}
```

2. execute `foggo foc` command.
2. execute `foggo fop` command.

```shell
# struct must be set struct type name
# package must be package path
$ foggo foc --struct Image --package image
$ foggo fop --struct Image --package image
```

3. then `foggo` generates Functional Option Pattern code to `./image/image_gen.go`.
Expand Down Expand Up @@ -120,7 +120,7 @@ Global Flags:
// ./image/image.go
package image
//go:generate foggo foc --struct Image
//go:generate foggo fop --struct Image
type Image struct {
Width int
Height int
Expand All @@ -138,17 +138,122 @@ Global Flags:

3. the `foggo` generate Functional Option Pattern code to all files written `go:generate`.

### Generate with `afop` command

`afop` is the method to generate `Applicable Functional Option Pattern` code.

1. prepare a struct type with `go:generate`. (use `afop` subcommand)

```go
// ./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. execute `go generate ./...` command.

```shell
$ go generate ./...
```

3. the `foggo` generate Applicable Functional Option Pattern code to all files written `go:generate`.

```go
// 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. write Golang code using `Applicable Functional Option Parameter`

```go
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` is one of the most common design patterns used in Golang code.
`Functional Option Pattern`(`FOP`) is one of the most common design patterns used in Golang code.

Golang cannot provide optional arguments such as keyword arguments (available in python, ruby, ...).
`Functional Option Pattern` is the technique for achieving optional arguments.
`FOP` is the technique for achieving optional arguments.

For more information, please refer to the following articles.

- 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 ?

`Applicable Functional Option Pattern`(`AFOP`) is __testable__ `FOP`.
`FOP` express options to function.
For that reason, comparing to option function with same arguments fails (not testable).

`AFOP` express options to struct type and options have a parameter and `apply` method.
Struct type is comparable in Golang, options followed `AFOP` are testable.

`AFOP` proposed by following articles.

- https://github.com/uber-go/guide/blob/master/style.md#functional-options
- https://ww24.jp/2019/07/go-option-pattern (in Japanese)

## References

- https://github.com/moznion/gonstructor
Loading

0 comments on commit 2e7ddcc

Please sign in to comment.