¿Puede ir tener parámetros opcionales? ¿O puedo simplemente definir dos funciones con el mismo nombre y un número diferente de argumentos?
¿Puede ir tener parámetros opcionales? ¿O puedo simplemente definir dos funciones con el mismo nombre y un número diferente de argumentos?
Respuestas:
Go no tiene parámetros opcionales ni admite la sobrecarga de métodos :
El envío de métodos se simplifica si no es necesario que también coincida con el tipo. La experiencia con otros idiomas nos dijo que tener una variedad de métodos con el mismo nombre pero con firmas diferentes ocasionalmente era útil, pero que también podría ser confuso y frágil en la práctica. Coincidir solo por nombre y requerir consistencia en los tipos fue una decisión simplificadora importante en el sistema de tipos de Go.
make
un caso especial, entonces? ¿O ni siquiera se implementa realmente como una función ...
make
es una construcción de lenguaje y las reglas mencionadas anteriormente no se aplican. Ver esta pregunta relacionada .
range
es el mismo caso que make
, en ese sentido
Una buena manera de lograr algo como parámetros opcionales es usar argumentos variadic. La función en realidad recibe una porción del tipo que especifique.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
es una porción de
Puede usar una estructura que incluya los parámetros:
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})
Para un número arbitrario y potencialmente grande de parámetros opcionales, un buen modismo es usar opciones funcionales .
Para su tipo Foobar
, primero escriba solo un constructor:
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
}
donde cada opción es una función que muta el Foobar. Luego, proporcione formas convenientes para que su usuario use o cree opciones estándar, por ejemplo:
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
}
}
Para ser concisos, puede dar un nombre al tipo de opciones ( Zona de juegos ):
type OptionFoobar func(*Foobar) error
Si necesita parámetros obligatorios, agréguelos como primeros argumentos del constructor antes de la variable options
.
Los principales beneficios de la idioma de opciones funcionales son:
Esta técnica fue acuñada por Rob Pike y también demostrada por Dave Cheney .
func()
s si fuera necesario, que doblar mi cerebro en torno a este enfoque. Cada vez que tengo que usar este enfoque, como en la biblioteca Echo, encuentro que mi cerebro queda atrapado en la madriguera de las abstracciones. #fwiw
Ni los parámetros opcionales ni la sobrecarga de funciones son compatibles con Go. Go admite un número variable de parámetros: pasar argumentos a ... parámetros
No, tampoco Según los documentos de Go para programadores de C ++ ,
Go no admite la sobrecarga de funciones y no admite operadores definidos por el usuario.
No puedo encontrar una declaración igualmente clara de que los parámetros opcionales no son compatibles, pero tampoco son compatibles.
Puede encapsular esto bastante bien en una función similar a la que se muestra a continuación.
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
}
En este ejemplo, la solicitud por defecto tiene dos puntos y un espacio delante. . .
:
. . . sin embargo, puede anular eso proporcionando un parámetro a la función de solicitud.
prompt("Input here -> ")
Esto dará como resultado un mensaje como el siguiente.
Input here ->
Terminé usando una combinación de una estructura de parámetros y argumentos variados. De esta manera, no tuve que cambiar la interfaz existente que fue consumida por varios servicios y mi servicio pudo pasar parámetros adicionales según fue necesario. Código de muestra en el patio de juegos de Golang: https://play.golang.org/p/G668FA97Nu
Llego un poco tarde, pero si te gusta la interfaz fluida, puedes diseñar tus setters para llamadas encadenadas como esta:
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
}
Y luego llámalo así:
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)
}
}
Esto es similar al lenguaje de opciones funcionales presentado en la respuesta @Ripounet y disfruta de los mismos beneficios, pero tiene algunos inconvenientes:
err
variable y poniéndola a cero.Sin embargo, existe una pequeña ventaja posible, este tipo de llamadas a funciones debería ser más fácil para el compilador en línea, pero realmente no soy un especialista.
Puede pasar parámetros nombrados arbitrarios con un mapa.
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})
}
Otra posibilidad sería utilizar una estructura que con un campo para indicar si es válida. Los tipos nulos de sql como NullString son convenientes. Es bueno no tener que definir su propio tipo, pero en caso de que necesite un tipo de datos personalizado, siempre puede seguir el mismo patrón. Creo que la opcionalidad está clara en la definición de la función y hay un mínimo de código o esfuerzo adicional.
Como ejemplo:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}