Goはオプションのパラメータを持つことができますか?それとも、同じ名前で引数の数が異なる2つの関数を定義するだけでいいのですか?
Goはオプションのパラメータを持っていません メソッドのオーバーロードをサポートしていません :
型のマッチングも必要ないのであれば、メソッドのディスパッチは単純化されます。他の言語での経験から、同じ名前で異なるシグネチャを持つさまざまな方法を使用すると便利な場合があることがわかりましたが、実際には混乱を招き、脆弱になる可能性もあります。名前だけで照合し、型の一貫性を要求することは、Goの型システムでは単純化された大きな決定でした。
オプションのパラメータのようなものを実現するための良い方法は、可変引数を使うことです。この関数は、実際には指定した型のスライスを受け取ります。
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
パラメータを含む構造体を使うことができます。
type Params struct {
a, b, c int
}
func doIt(p Params) int {
return p.a + p.b + p.c
}
// you can call it without specifying all parameters
doIt(Params{a: 1, c: 9})
任意の、潜在的に多数のオプションパラメータに対して、Niceという慣用句は機能オプションを使うことです。
あなたの型Foobar
には、まずコンストラクタを1つだけ書いてください。
func NewFoobar(options ...func(*Foobar) error) (*Foobar, error){
fb := &Foobar{}
// ... (write initializations with default values)...
for _, op := range options{
err := op(fb)
if err != nil {
return nil, err
}
}
return fb, nil
}
各オプションはFoobarを変化させる関数です。次に、ユーザーが標準オプションを使用または作成するための便利な方法を提供します。次に例を示します。
func OptionReadonlyFlag(fb *Foobar) error {
fb.mutable = false
return nil
}
func OptionTemperature(t Celsius) func(*Foobar) error {
return func(fb *Foobar) error {
fb.temperature = t
return nil
}
}
簡潔にするために、オプションのタイプに名前を付けることができます( Playground )。
type OptionFoobar func(*Foobar) error
必須パラメータが必要な場合は、可変個のoptions
の前にコンストラクタの最初の引数として追加します。
機能オプションイディオムの主な利点は次のとおりです。
このテクニックは Rob Pike によって造られ、 Dave Cheney によっても実証されました。
Goでは、オプションのパラメータも関数のオーバーロードもサポートされていません。 Goは可変数のパラメータをサポートしています。 ...引数に引数を渡す
いいえ - どちらでもない。によると C++プログラマのために行く ドキュメント、
Goは関数のオーバーロードをサポートしておらず、ユーザー定義演算子もサポートしていません。
オプションのパラメータはサポートされていないという同じように明確な文を見つけることはできませんが、それらもサポートされていません。
以下のような機能でこれを非常にうまくカプセル化できます。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Println(Prompt())
}
func Prompt(params ...string) string {
Prompt := ": "
if len(params) > 0 {
Prompt = params[0]
}
reader := bufio.NewReader(os.Stdin)
fmt.Print(Prompt)
text, _ := reader.ReadString('\n')
return text
}
この例では、デフォルトでプロンプトの前にコロンとスペースがあります。 。 。
:
。 。 。ただし、Prompt関数にパラメータを指定することでこれをオーバーライドできます。
Prompt("Input here -> ")
これにより、以下のようなプロンプトが表示されます。
Input here ->
Go言語はメソッドのオーバーロードをサポートしていませんが、オプションのパラメータと同じように可変引数を使用できます。また、パラメータとしてinterface {}を使用することもできますが、これは適切な選択ではありません。
結局、paramsとvariadic argsの構造の組み合わせを使うことになりました。この方法で、私はいくつかのサービスによって消費されていた既存のインターフェースを変更する必要はなく、私のサービスは必要に応じて追加のパラメータを渡すことができました。遊び場でのサンプルコード: https://play.golang.org/p/G668FA97N
マップで任意の名前付きパラメーターを渡すことができます。
type varArgs map[string]interface{}
func myFunc(args varArgs) {
arg1 := "default" // optional default value
if val, ok := args["arg1"]; ok {
// value override or other action
arg1 = val.(string) // runtime panic if wrong type
}
arg2 := 123 // optional default value
if val, ok := args["arg2"]; ok {
// value override or other action
arg2 = val.(int) // runtime panic if wrong type
}
fmt.Println(arg1, arg2)
}
func Test_test() {
myFunc(varArgs{"arg1": "value", "arg2": 1234})
}
私は少し遅れています、しかしあなたが流暢なインターフェースを好むならば、あなたはこのような連鎖された呼び出しのためにあなたのセッターを設計するかもしれません:
type myType struct {
s string
a, b int
}
func New(s string, err *error) *myType {
if s == "" {
*err = errors.New(
"Mandatory argument `s` must not be empty!")
}
return &myType{s: s}
}
func (this *myType) setA (a int, err *error) *myType {
if *err == nil {
if a == 42 {
*err = errors.New("42 is not the answer!")
} else {
this.a = a
}
}
return this
}
func (this *myType) setB (b int, _ *error) *myType {
this.b = b
return this
}
そしてそれを次のように呼びます。
func main() {
var err error = nil
instance :=
New("hello", &err).
setA(1, &err).
setB(2, &err)
if err != nil {
fmt.Println("Failed: ", err)
} else {
fmt.Println(instance)
}
}
これは@Ripounetの回答で示された機能オプションイディオムに似ており、同じ利点を享受しますが、いくつかの欠点があります。
err
変数を宣言してゼロにする行を費やす必要があります。ただし、小さな利点もあります。この種の関数呼び出しは、コンパイラーにとってインライン化するほうが簡単なはずですが、私は本当に専門家ではありません。