Bisakah Go memiliki parameter opsional? Atau bisakah saya mendefinisikan dua fungsi dengan nama yang sama dan jumlah argumen yang berbeda?
Bisakah Go memiliki parameter opsional? Atau bisakah saya mendefinisikan dua fungsi dengan nama yang sama dan jumlah argumen yang berbeda?
Jawaban:
Go tidak memiliki parameter opsional dan juga tidak mendukung metode overloading :
Metode pengiriman disederhanakan jika tidak perlu melakukan pencocokan jenis juga. Pengalaman dengan bahasa lain memberi tahu kami bahwa memiliki berbagai metode dengan nama yang sama tetapi tanda tangan yang berbeda kadang-kadang berguna tetapi juga bisa membingungkan dan rapuh dalam praktiknya. Mencocokkan hanya dengan nama dan membutuhkan konsistensi dalam tipe adalah keputusan penyederhanaan utama dalam sistem tipe Go.
make
ada kasus khusus? Atau bahkan tidak benar-benar diimplementasikan sebagai fungsi ...
make
adalah konstruksi bahasa dan aturan yang disebutkan di atas tidak berlaku. Lihat pertanyaan terkait ini .
range
adalah kasus yang sama dengan make
, dalam arti
Cara yang bagus untuk mencapai sesuatu seperti parameter opsional adalah dengan menggunakan args variadic. Fungsi sebenarnya menerima sepotong tipe apa pun yang Anda tentukan.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
adalah sepotong int
Anda dapat menggunakan struct yang mencakup parameter:
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})
Untuk parameter opsional yang sewenang-wenang dan berpotensi besar, idiom yang bagus adalah menggunakan opsi Fungsional .
Untuk tipe Anda Foobar
, tulis pertama hanya satu konstruktor:
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
}
di mana setiap opsi adalah fungsi yang mengubah Foobar. Kemudian berikan cara mudah bagi pengguna Anda untuk menggunakan atau membuat opsi standar, misalnya:
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
}
}
Untuk keringkasan, Anda dapat memberi nama dengan jenis opsi ( Taman Bermain ):
type OptionFoobar func(*Foobar) error
Jika Anda membutuhkan parameter wajib, tambahkan sebagai argumen pertama konstruktor sebelum variadic options
.
Manfaat utama dari idiom opsi Fungsional adalah:
Teknik ini diciptakan oleh Rob Pike dan juga didemonstrasikan oleh Dave Cheney .
func()
jika perlu, daripada itu menekuk otak saya di sekitar pendekatan ini. Setiap kali saya harus menggunakan pendekatan ini, seperti dengan perpustakaan Echo, saya menemukan otak saya terjebak dalam lubang kelinci abstraksi. #fwiw
Parameter opsional atau kelebihan fungsi tidak didukung di Go. Go memang mendukung sejumlah variabel parameter: Melewati argumen ke ... parameter
Tidak - tidak juga. Per the Go for C ++ programmer programmer ,
Go tidak mendukung fungsi yang berlebihan dan tidak mendukung operator yang ditentukan pengguna.
Saya tidak dapat menemukan pernyataan yang sama jelasnya bahwa parameter opsional tidak didukung, tetapi mereka juga tidak didukung.
Anda dapat merangkum ini dengan sangat baik dalam fungsi yang mirip dengan apa yang ada di bawah ini.
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
}
Dalam contoh ini, prompt secara default memiliki titik dua dan spasi di depannya. . .
:
. . . namun Anda dapat menimpanya dengan menyediakan parameter ke fungsi prompt.
prompt("Input here -> ")
Ini akan menghasilkan prompt seperti di bawah ini.
Input here ->
Saya akhirnya menggunakan kombinasi struktur params dan variadic args. Dengan cara ini, saya tidak perlu mengubah antarmuka yang ada yang dikonsumsi oleh beberapa layanan dan layanan saya dapat melewati params tambahan sesuai kebutuhan. Contoh kode di taman bermain golang: https://play.golang.org/p/G668FA97Nu
Saya sedikit terlambat, tetapi jika Anda menyukai antarmuka yang lancar, Anda dapat mendesain setter Anda untuk panggilan berantai seperti ini:
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
}
Dan menyebutnya seperti ini:
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)
}
}
Ini mirip dengan idiom opsi Fungsional yang disajikan pada jawaban @Rounet dan menikmati manfaat yang sama tetapi memiliki beberapa kelemahan:
err
variabel dan mem-nolkannya.Namun, ada sedikit kemungkinan keuntungan, pemanggilan fungsi jenis ini seharusnya lebih mudah bagi kompiler untuk inline tetapi saya benar-benar bukan spesialis.
Anda dapat melewati parameter bernama acak dengan peta.
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})
}
Kemungkinan lain adalah menggunakan struct yang dengan bidang untuk menunjukkan apakah valid. Tipe null dari sql seperti NullString nyaman. Sangat menyenangkan tidak harus mendefinisikan tipe Anda sendiri, tetapi jika Anda membutuhkan tipe data khusus, Anda selalu dapat mengikuti pola yang sama. Saya pikir opsional-ness jelas dari definisi fungsi dan ada kode atau usaha ekstra minimal.
Sebagai contoh:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}