Optionalな引数、どうしてますか?
pythonやjavascriptであれば
def hoge(a, b="bbb", c=123):
pass
こんな風にすれば、関数はoptionalな引数を持つことができますが、Golangではこういう文法はありません。
調べると「Functional Optionを使いましょう」という記述に沢山出会います。
実際にFunctional Optionってどうやればいいんだろう?ということでやってみました。
実装
クラスのコンストラクタの場合
struct Sample {
a string
b string
c int
}
func (s *Sample) init(){
// init Sample
}
func NewSample(a string, b string, c int) *Sample {
s := &Sample{a,b,c}
s.init()
return s
}
たとえばこんなコードがあるとします。
b,c をFunctional Optionにするとこんな感じになります。
type Sample struct {
a string
b string
c int
}
func (s *Sample) init(){
// init Sample
}
func NewSample(a string, options ...Option) *Sample {
s := &Sample{a, defaultB, defaultC}
for _, f := range options {
f(s)
}
s.init()
return s
}
type Option func(s *Sample)
func WithB(b string) Option {
return func(s *Sample){
s.b = b
}
}
func WithC(c int) Option {
return func(s *Sample){
s.c = c
}
}
実行
func main(){
s := NewSample("aaa", WithB("bbb"), WithC(123))
}
こんな風にすれば、nilのような不要なパラメータが出現する事もなく、type safeなgolangの特徴を崩す事もなくoptionを導入できそうです。
関数の場合
関数では、クラスのように状態を持つことができるstructが存在しないため、新たに導入する必要がありそうです。
func hoge(a string, b string, c int){
// do something with a,b,c
}
上記関数に対して、b,cをFunctional Optionにしてみます。
func hoge(a string, options ...Option){
o := &option{defaultB, defaultC}
for _, f := range options {
f(o)
}
// do something with a, o.b, o.c
}
type option struct {
b string
c int
}
type Option func(o *option)
func WithB(b string) Option {
return func(o *option){
o.b = b
}
}
func WithC(c int) Option {
return func(o *option){
o.c = c
}
}
実行
func main(){
hoge("aaa", WithB("bbb"), WithC(123))
}
こんな風にoption structを導入することで、クラスのコンストラクタと同様な手順で関数のオプションをFunctional Option化することができそうです。
optionというstructが導入されましたが、無駄な設定がなくtype safeなgolangの特徴を壊していないかと思います。
感想
最近仕事でgolangを書き始めました。
独特なルールを持つ言語なので、golangの特徴を生かした書き方をいろいろ探してみたいと思っています。
参考
https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html
https://medium.com/@dreissenzahn/functional-options-in-go-35279f535c6