サマリ
- 実務においてFunctional Options Patternを活用できた例を紹介します
- 具体的には、構造体が階層構造になっている場合で、初期化時の子要素の構造体作成の制御に利用したケースを紹介します
はじめに
Goを実務で使うようになって、学び始めてから1年以上経ちましたが、なかなかアウトプットができていなかったので初めて記事を書いてみました。
今回は、GoのFunctional Options Patternについて、実務で利用できたケースについて紹介できればと思います。
Functional Options Patternとは?
Functional Options Patternは、Go言語でよく使われるデザインパターンの一つです。
構造体の初期化時に、特定のオプションフィールドを関数として渡すことにより、構造体の設定を行うものです。
このパターンを用いると、構造体の初期化時に多くの引数を渡す必要がある場合でも、可読性を損なわずにオプションを追加・管理することが可能になります。
今回は詳細の解説は行いませんが、以下の記事がシンプルにまとまっていて良かったですので、参考にリンクさせていただきます。
構造体が階層構造になっている場合で、初期化時の子要素の構造体作成の制御に利用したケース
実務においてFunctional Options Patternがうまくで活用できた事例として、構造体が階層構造になっている場合における、初期化時の子要素の構造体作成の制御に利用したケースをコードサンプルを交えてご紹介します。
まず、親要素と子要素からなる、以下のような階層構造の構造体を考えます。
type parent struct {
id int
name string
children []child
}
type child struct {
id int
name string
}
childrenはparentのオプショナルなフィールドになるので、このパターンはFunctional Options Patternと相性が良さそうです。
早速options構造体とoption関数を作成してみましょう。
type parentOptions struct {
children []child
}
type parentOption func(opts *parentOptions)
func WithChild(child child) parentOption {
return func(opts *parentOptions) {
opts.children = append(opts.children, child)
}
}
WithChild関数により、childを必要に応じて追加した上でparentを初期化することを意図しています。
続いて、初期化の関数も見ていきましょう。
func NewParent(id int, name string, opts ...parentOption) parent {
options := parentOptions{}
for _, opt := range opts {
opt(&options)
}
p := parent{
id: id,
name: name,
}
if len(options.children) > 0 {
p.children = options.children
}
return p
}
最後に利用例を見ます。
func main() {
// parent without child
p1 := NewParent(1, "parent1")
_ = p1
// parent with child
p2 := NewParent(2, "parent2",
WithChild(child{id: 1, name: "child1"}),
WithChild(child{id: 2, name: "child2"}))
_ = p2
}
childrenが不要な場合は初期化時に記載を省略できますし、childが必要な場合は、適切にchildをユースケースに合わせた数追加できます。
Functional Options Patternの1つの良い利用例ではないかなと考えています。
皆さんのコーディングのインスピレーションになれば幸いです。
それではまた👋