O Go pode ter parâmetros opcionais? Ou posso apenas definir duas funções com o mesmo nome e um número diferente de argumentos?
O Go pode ter parâmetros opcionais? Ou posso apenas definir duas funções com o mesmo nome e um número diferente de argumentos?
Respostas:
O Go não possui parâmetros opcionais nem suporta sobrecarga de método :
O envio de métodos é simplificado se não for necessário fazer a correspondência de tipos também. A experiência com outros idiomas nos disse que ter uma variedade de métodos com o mesmo nome, mas com assinaturas diferentes, era ocasionalmente útil, mas também poderia ser confuso e frágil na prática. Combinar apenas pelo nome e exigir consistência nos tipos foi uma decisão importante e simplificadora no sistema de tipos da Go.
make
um caso especial, então? Ou não é mesmo realmente implementado como uma função ...
make
é uma construção de linguagem e as regras mencionadas acima não se aplicam. Veja esta pergunta relacionada .
range
é o mesmo caso make
, nesse sentido
Uma boa maneira de obter algo como parâmetros opcionais é usar argumentos variados. A função realmente recebe uma fatia do tipo que você especificar.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
é uma fatia de ints
Você pode usar uma estrutura que inclui os 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 um número arbitrário e potencialmente grande de parâmetros opcionais, um bom idioma é usar as opções funcionais .
Para seu tipo Foobar
, primeiro escreva apenas um construtor:
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
}
onde cada opção é uma função que modifica o Foobar. Em seguida, forneça maneiras convenientes para o usuário usar ou criar opções padrão, por exemplo:
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
}
}
Por uma questão de concisão, você pode dar um nome ao tipo de opções ( Playground ):
type OptionFoobar func(*Foobar) error
Se você precisar de parâmetros obrigatórios, inclua-os como primeiros argumentos do construtor antes da variável options
.
Os principais benefícios do idioma das opções funcionais são:
Essa técnica foi cunhada por Rob Pike e também demonstrada por Dave Cheney .
func()
s, se necessário, do que dobrar meu cérebro em torno dessa abordagem. Sempre que tenho que usar essa abordagem, como na biblioteca Echo, encontro meu cérebro preso na toca do coelho das abstrações. #wiw #
Nem parâmetros opcionais nem sobrecarga de função são suportados no Go. Go suporta um número variável de parâmetros: Passando argumentos para ... parâmetros
Não - nem. De acordo com os documentos dos programadores do Go for C ++ ,
O Go não suporta sobrecarga de função e não suporta operadores definidos pelo usuário.
Não consigo encontrar uma declaração igualmente clara de que parâmetros opcionais não são suportados, mas eles também não são suportados.
Você pode encapsular isso muito bem em uma função semelhante ao que está abaixo.
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
}
Neste exemplo, o prompt por padrão possui dois pontos e um espaço à sua frente. . .
:
. . . no entanto, você pode substituir isso fornecendo um parâmetro para a função prompt.
prompt("Input here -> ")
Isso resultará em um prompt como abaixo.
Input here ->
Acabei usando uma combinação de uma estrutura de parâmetros e argumentos variáveis. Dessa forma, não precisei alterar a interface existente consumida por vários serviços e meu serviço conseguiu passar parâmetros adicionais conforme necessário. Código de exemplo no playground de golang: https://play.golang.org/p/G668FA97Nu
Estou um pouco atrasado, mas se você gosta de uma interface fluente, pode projetar seus setters para chamadas encadeadas 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
}
E então chame assim:
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)
}
}
Isso é semelhante ao idioma das opções funcionais apresentado na resposta do @Ripounet e possui os mesmos benefícios, mas tem algumas desvantagens:
err
variável e zerando-a.Há, no entanto, uma pequena vantagem possível: esse tipo de chamada de função deve ser mais fácil para o compilador alinhar, mas eu realmente não sou especialista.
Você pode passar parâmetros nomeados arbitrários com um 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})
}
Outra possibilidade seria usar um struct que com um campo para indicar se é válido. Os tipos nulos do sql, como NullString, são convenientes. É bom não ter que definir seu próprio tipo, mas caso você precise de um tipo de dados personalizado, sempre poderá seguir o mesmo padrão. Acho que a opcionalidade é clara na definição da função e há um mínimo de código ou esforço extra.
Como um exemplo:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}