Go peut-il avoir des paramètres optionnels? Ou puis-je simplement définir deux fonctions avec le même nom et un nombre différent d'arguments?
Go n'a pas de paramètre optionnel il ne supporte pas non plus la surcharge de méthode :
L'envoi de méthode est simplifié s'il n'est pas nécessaire de faire la correspondance de type. L’expérience d’autres langues nous a appris qu’avoir recours à diverses méthodes du même nom mais avec des signatures différentes était parfois utile, mais que cela pouvait aussi être source de confusion et de fragilité dans la pratique. Faire correspondre uniquement par nom et nécessiter une cohérence dans les types était une décision de simplification majeure dans le système de types de Go.
Une méthode intéressante pour obtenir des paramètres facultatifs consiste à utiliser des arguments variadiques. La fonction reçoit en réalité une tranche du type que vous spécifiez.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
Vous pouvez utiliser une structure qui inclut les paramètres:
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})
Pour un nombre arbitraire et potentiellement important de paramètres facultatifs, un idiome de Nice doit utiliser Options fonctionnelles .
Pour votre type Foobar
, écrivez d'abord un seul constructeur:
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
}
où chaque option est une fonction qui mue le Foobar. Fournissez ensuite à votre utilisateur des moyens pratiques d’utiliser ou de créer des options standard, par exemple:
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
}
}
Par souci de concision, vous pouvez nommer le type des options ( Playground ):
type OptionFoobar func(*Foobar) error
Si vous avez besoin de paramètres obligatoires, ajoutez-les en tant que premiers arguments du constructeur, avant la variable options
.
Les principaux avantages de l'idiome des options fonctionnelles sont les suivants:
Cette technique a été inventée par Rob Pike et également démontrée par Dave Cheney .
Ni les paramètres facultatifs ni la surcharge de fonctions ne sont pris en charge par Go. Go supporte un nombre variable de paramètres: Passage d'arguments à ... paramètres
Non - ni Par le Go pour les programmeurs C++ docs,
Go ne prend pas en charge la surcharge de fonctions ni les opérateurs définis par l'utilisateur.
Je ne trouve pas d'affirmation tout aussi claire que les paramètres facultatifs ne sont pas pris en charge, mais ils ne le sont pas non plus.
Vous pouvez encapsuler cela assez bien dans une fonction similaire à celle décrite ci-dessous.
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
}
Dans cet exemple, l'invite par défaut est précédée de deux points et d'un espace. . .
:
. . . Cependant, vous pouvez le remplacer en fournissant un paramètre à la fonction Invite.
Prompt("Input here -> ")
Cela se traduira par une invite comme ci-dessous.
Input here ->
Go language ne prend pas en charge la surcharge de méthodes, mais vous pouvez utiliser des arguments variadiques comme des paramètres facultatifs. Vous pouvez également utiliser l'interface {} comme paramètre, mais ce n'est pas un bon choix.
J'ai fini par utiliser une combinaison d'une structure de paramètres et d'arguments variadiques. De cette façon, je n'ai pas eu à changer l'interface existante utilisée par plusieurs services et mon service a pu transmettre des paramètres supplémentaires en fonction des besoins. Exemple de code dans la zone de jeux golang: https://play.golang.org/p/G668FA97N
Je suis un peu en retard, mais si vous aimez l'interface fluide, vous pouvez concevoir vos paramètres pour les appels chaînés comme ceci:
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
}
Et puis appelez ça comme ça:
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)
}
}
Ceci est similaire au Options fonctionnelles présenté dans la réponse @Ripounet et bénéficie des mêmes avantages, mais présente certains inconvénients:
err
et à la mettre à zéro.Il y a cependant un petit avantage possible, ce type d'appels de fonction devrait être plus facile à intégrer pour le compilateur, mais je ne suis vraiment pas un spécialiste.
Vous pouvez passer des paramètres nommés de manière arbitraire avec une carte.
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})
}