围棋中的建造者

I have a struct and I would like it to be initialised with some sensible default values.

Typically, the thing to do here is to use a constructor but since go isn't really OOP in the traditional sense these aren't true objects and it has no constructors.

I have noticed the init method but that is at the package level. Is there something else similar that can be used at the struct level?

If not what is the accepted best practice for this type of thing in Go?

转载于:https://stackoverflow.com/questions/18125625/constructors-in-go

8个回答

There are actually two accepted best practices:

  1. Make the zero value of your struct a sensible default. (While this looks strange to most people coming from "traditional" oop it often works and is really convenient).
  2. Provide a function func New() YourTyp or if you have several such types in your package functions func NewYourType1() YourType1 and so on.

Document if a zero value of your type is usable or not (in which case it has to be set up by one of the New... functions. (For the "traditionalist" oops: Someone who does not read the documentation won't be able to use your types properly, even if he cannot create objects in undefined states.)

csdnceshi66
必承其重 | 欲带皇冠 Yes and no, it depends. Most probably yes, provide a func New() T. But depending on the case you could check for this nil map and make one only once it is needed. In this case: Document if this map creation is safe for concurrent use (aka your code which makes the map is guarded e.g. by a mutex.). Depends a bit whether the map is exported or not... Hard to tell without seeing code.
大约 7 年之前 回复
csdnceshi79
python小菜 how would that work for properties such as maps. The default value for this is nil, right? Therefore should these always be initialised via a New function?
大约 7 年之前 回复

There are some equivalents of constructors for when the zero values can't make sensible default values or for when some parameter is necessary for the struct initialization.

Supposing you have a struct like this :

type Thing struct {
    Name  string
    Num   int
}

then, if the zero values aren't fitting, you would typically construct an instance with a NewThing function returning a pointer :

func NewThing(someParameter string) *Thing {
    p := new(Thing)
    p.Name = someParameter
    p.Num = 33 // <- a very sensible default value
    return p
}

When your struct is simple enough, you can use this condensed construct :

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33}
}

If you don't want to return a pointer, then a practice is to call the function makeThing instead of NewThing :

func makeThing(name string) Thing {
    return Thing{name, 33}
}

Reference : Allocation with new in Effective Go.

weixin_41568127
?yb? I think this approach is called "Factory function".
2 年多之前 回复
weixin_41568131
10.24 It depends
4 年多之前 回复
csdnceshi62
csdnceshi62 Is it preferrable to return a pointer or the struct, and why?
4 年多之前 回复
weixin_41568184
叼花硬汉 new is normally used if you do not want to initialize anything. A common example is c := new(math.Big).Add(a, b) which would look extremely ugly with struct literals...
大约 7 年之前 回复
weixin_41568131
10.24 You're right. And I admit it's often clearer to use a struct literal. But you'll also find new(Thing) in standard packages. But I'll edit a little my answer.
大约 7 年之前 回复
weixin_41568184
叼花硬汉 Yes, but the following paragraph of "Effective Go" introduces struct literals and demonstrate how they can be used to write a more idiomatic version of the NewFile constructor :)
大约 7 年之前 回复
weixin_41568131
10.24 well... EffectiveGo gives an example of a NewFile constructor using new(File)...
大约 7 年之前 回复
weixin_41568184
叼花硬汉 It's not very common to allocate structs with new and set the values afterwards. Struct literals are the preferred way there. And I am not sure about your "makeThing" naming convention either. The standard library calls the constructors New() or NewThing() consistently and I have never encountered any makeThing() functions my self...
大约 7 年之前 回复
weixin_41568131
10.24 I'm not sure of what you mean. Having a NewThing function is something standard. If you mean they're not automatically called, yes, but you can't automatically use structs anyway. I don't think you should try to hide those constructors with interfaces, the code is clearer when they appear.
大约 7 年之前 回复
csdnceshi72
谁还没个明天 Ok so this makes sense but it implies that clients of these must know about the New and make functions. i.e. this is not standard among all structs. I imagine that can be handled with interfaces
大约 7 年之前 回复

another way is;

package person

type Person struct {
    Name string
    Old  int
}

func New(name string, old int) *Person {
    // set only specific field value with field key
    return &Person{
        Name: name,
    }
}
csdnceshi72
谁还没个明天 person wasn't a variable, but the package :) So New is a func, not a method.
2 年多之前 回复
csdnceshi70
笑故挽风 I agree with this though: "no, because that's the main (possibly only) type of the package. "
2 年多之前 回复
csdnceshi70
笑故挽风 but the function New is not a method of Person Struct so you cant call person.New
2 年多之前 回复
csdnceshi72
谁还没个明天 no, because that's the main (possibly only) type of the package. You'd use it as person.New(name, old). Compare with person.NewPerson(name, old), which stutters.
2 年多之前 回复
csdnceshi70
笑故挽风 shouldn't it be NewPerson instead of New ?
2 年多之前 回复

I like the explanation from this blog post:

The function New is a Go convention for packages that create a core type or different types for use by the application developer. Look at how New is defined and implemented in log.go, bufio.go and cypto.go:

log.go

// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) * Logger {
    return &Logger{out: out, prefix: prefix, flag: flag}
}

bufio.go

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) * Reader {
    return NewReaderSize(rd, defaultBufSize)
}

crypto.go

// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
    if h > 0 && h < maxHash {
        f := hashes[h]
        if f != nil {
            return f()
        }
    }
    panic("crypto: requested hash function is unavailable")
}

Since each package acts as a namespace, every package can have their own version of New. In bufio.go multiple types can be created, so there is no standalone New function. Here you will find functions like NewReader and NewWriter.

Go has objects. Objects can have constructors (although not automatic constructors). And finally, Go is an OOP language (data types have methods attached, but admittedly there are endless definitions of what OOP is.)

Nevertheless, the accepted best practice is to write zero or more constructors for your types.

As @dystroy posted his answer before me finishing this answer, let me just add an alternative version of his example constructor, which I would probably write instead as:

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33} // <- 33: a very sensible default value
}

The reason I want to show you this version is that pretty often "inline" literals can be used instead of a "constructor" call.

a := NewThing("foo")
b := &Thing{"foo", 33}

Now *a == *b.

csdnceshi71
Memor.の a != b, but *a == *b because the structs they point to have equal fields, see play.golang.org/p/A3ed7wNVVA for an example
6 年多之前 回复
weixin_41568184
叼花硬汉 Can you add more explain about why a and b will be equal?
大约 7 年之前 回复
csdnceshi75
衫裤跑路 I think that was the case. And thanks ;-)
大约 7 年之前 回复
csdnceshi58
Didn"t forge +1 because of the equality thing. It might be a little (or totally) off-topic but it's important. I think it came with Go1, no ?
大约 7 年之前 回复

There are no default constructors in Go, but you can declare methods for any type. You could make it a habit to declare a method called "Init". Not sure if how this relates to best practices, but it helps keep names short without loosing clarity.

package main

import "fmt"

type Thing struct {
    Name string
    Num int
}

func (t *Thing) Init(name string, num int) {
    t.Name = name
    t.Num = num
}

func main() {
    t := new(Thing)
    t.Init("Hello", 5)
    fmt.Printf("%s: %d\n", t.Name, t.Num)
}

The result is:

Hello: 5
weixin_41568110
七度&光 Question: semantically, t := new(Thing) \n t.Init(...) is the same as var t Thing \n t.Init(...), right? Which of the forms is seen as more idiomatic in Go?
3 年多之前 回复

Golang is not OOP language in its official documents. All fields of Golang struct has a determined value(not like c/c++), so constructor function is not so necessary as cpp. If you need assign some fields some special values, use factory functions. Golang's community suggest New.. pattern names.

csdnceshi54
hurriedly% you may require constructor functions if your struct fields require to be initialized first
2 年多之前 回复

If you want to emulate ___.new() syntax you can do something along the lines of:

type Thing struct {
    Name string
    Num int
}
type Constructor_Thing struct {}
func (c CThing) new(<<CONSTRUCTOR ARGS>>) Thing {
  var thing Thing
  //initiate thing from constructor args
  return thing
}
var cThing CThing

func main(){
  var myThing Thing
  myThing = cThing.new(<<CONSTRUCTOR ARGS>>)
  //...
}

Granted, it is a shame that Thing.new() cannot be implemented without CThing.new() also being implemented (iirc) which is a bit of a shame...

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐