Constructors in Go

huangapple go评论88阅读模式
英文:

Constructors in Go

问题

我有一个结构体,我希望它能够用一些合理的默认值进行初始化。

通常情况下,可以使用构造函数来实现这个目的,但是由于Go并不是传统意义上的面向对象编程语言,它没有构造函数。

我注意到了init方法,但是它是在包级别上的。在结构体级别上是否有类似的方法可以使用?

如果没有,那么在Go中,对于这种情况,有什么被广泛接受的最佳实践呢?

英文:

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?

答案1

得分: 299

当零值不能产生合理的默认值或某些参数对于结构体初始化是必需的时,可以使用构造函数的一些等价方式。

假设你有一个如下的结构体:

type Thing struct {
    Name  string
    Num   int
}

如果零值不适用,通常你会使用一个返回指针的NewThing函数来构造一个实例:

func NewThing(someParameter string) *Thing {
    p := new(Thing)
    p.Name = someParameter
    p.Num = 33 // <- 一个非常合理的默认值
    return p
}

当你的结构体足够简单时,你可以使用这种简洁的构造方式:

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

如果你不想返回一个指针,那么一种惯例是将函数命名为makeThing而不是NewThing

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

参考:Effective Go中的new分配

英文:

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 // &lt;- 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 &amp;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.

答案2

得分: 176

实际上有两种被接受的最佳实践:

  1. 将结构体的零值设置为一个合理的默认值。(虽然对于大多数从“传统”面向对象编程转过来的人来说这看起来很奇怪,但它通常有效且非常方便)。
  2. 提供一个函数 func New() YourTyp,或者如果你的包中有多个这样的类型,则提供函数 func NewYourType1() YourType1 等等。

如果你的类型的零值可用或不可用,需要在文档中进行说明(在这种情况下,它必须由其中一个 New... 函数进行设置)。对于“传统派”的面向对象编程者来说:即使他无法创建处于未定义状态的对象,如果他不阅读文档,也无法正确使用你的类型。

英文:

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.)

答案3

得分: 57

Go有对象。对象可以有构造函数(尽管没有自动构造函数)。最后,Go是一种面向对象的语言(数据类型附带方法,但是对于面向对象编程的定义有无数种)。

然而,被广泛接受的最佳实践是为您的类型编写零个或多个构造函数。

在@dystroy在我完成这个答案之前发布了他的答案之后,让我添加一个他的示例构造函数的替代版本,我可能会写成这样:

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33} // <- 33:一个非常合理的默认值
}

我想展示给你这个版本的原因是,很多时候可以使用“内联”字面值代替“构造函数”调用。

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

现在 *a == *b

英文:

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 I finished 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 &amp;Thing{someParameter, 33} // &lt;- 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(&quot;foo&quot;)
b := &amp;Thing{&quot;foo&quot;, 33}

Now *a == *b.

答案4

得分: 15

在Go语言中,没有默认构造函数,但你可以为任何类型声明方法。你可以养成声明一个名为"Init"的方法的习惯。不确定这与最佳实践有何关联,但它有助于保持名称简短而不失清晰。

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)
}

结果是:

Hello: 5
英文:

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 &quot;fmt&quot;

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(&quot;Hello&quot;, 5)
	fmt.Printf(&quot;%s: %d\n&quot;, t.Name, t.Num)
}

The result is:

Hello: 5

答案5

得分: 14

我喜欢这篇博客文章中的解释:

> New函数是Go语言中用于创建核心类型或不同类型以供应用程序开发人员使用的约定。看一下log.go、bufio.go和crypto.go中如何定义和实现New函数:

log.go

// New函数创建一个新的Logger。out变量设置日志数据将被写入的目标。
// prefix出现在每个生成的日志行的开头。
// flag参数定义了日志的属性。
func New(out io.Writer, prefix string, flag int) *Logger {
    return &amp;Logger{out: out, prefix: prefix, flag: flag}
}

bufio.go

// NewReader返回一个具有默认大小缓冲区的新Reader。
func NewReader(rd io.Reader) *Reader {
    return NewReaderSize(rd, defaultBufSize)
}

crypto.go

// New函数返回一个计算给定哈希函数的新hash.Hash。如果哈希函数未链接到二进制文件中,则New函数会引发panic。
func (h Hash) New() hash.Hash {
    if h &gt; 0 &amp;&amp; h &lt; maxHash {
        f := hashes[h]
        if f != nil {
            return f()
        }
    }
    panic(&quot;crypto: requested hash function is unavailable&quot;)
}

> 由于每个包都充当命名空间,因此每个包都可以有自己的New版本。在bufio.go中可以创建多个类型,因此没有独立的New函数。在这里,你会找到像NewReader和NewWriter这样的函数。

英文:

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 &amp;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 &gt; 0 &amp;&amp; h &lt; maxHash {
        f := hashes[h]
        if f != nil {
            return f()
        }
    }
    panic(&quot;crypto: requested hash function is unavailable&quot;)
}

> 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.

答案6

得分: 9

在Go语言中,可以使用一个返回指向修改后的结构体的指针的函数来实现构造函数。

type Colors struct {
    R   byte
    G   byte
    B   byte
}

// 构造函数
func NewColors(r, g, b byte) *Colors {
    return &Colors{R: r, G: g, B: b}
}

为了实现弱依赖和更好的抽象,构造函数不返回一个指向结构体的指针,而是返回该结构体实现的接口。

type Painter interface {
    paintMethod1() byte
    paintMethod2(byte) byte
}

type Colors struct {
    R byte
    G byte
    B byte
}

// 构造函数返回接口
func NewColors(r, g, b byte) Painter {
    return &Colors{R: r, G: g, B: b}
}

func (c *Colors) paintMethod1() byte {
    return c.R
}

func (c *Colors) paintMethod2(b byte) byte {
    c.B = b
    return c.B
}
英文:

In Go, a constructor can be implemented using a function that returns a pointer to a modified structure.

type Colors struct {
    R   byte
    G   byte
    B   byte
}

// Constructor
func NewColors (r, g, b byte) *Colors {
    return &amp;Color{R:r, G:g, B:b}
}

For weak dependencies and better abstraction, the constructor does not return a pointer to a structure, but an interface that this structure implements.

type Painter interface {
    paintMethod1() byte
    paintMethod2(byte) byte
}

type Colors struct {
    R byte
    G byte
    B byte
}

// Constructor return intreface
func NewColors(r, g, b byte) Painter {
    return &amp;Color{R: r, G: g, B: b}
}

func (c *Colors) paintMethod1() byte {
    return c.R
}

func (c *Colors) paintMethod2(b byte) byte {
    return c.B = b
}

答案7

得分: 6

另一种方法是:

package person

type Person struct {
    Name string
    Old  int
}

func New(name string, old int) *Person {
    // 使用字段键仅设置特定字段的值
    return &Person{
        Name: name,
    }
}
英文:

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 &amp;Person{
        Name: name,
    }
}

答案8

得分: 5

如果你想强制使用工厂函数,将你的结构体(或类)命名为首字母小写。这样,就无法直接实例化结构体,而必须使用工厂方法。

这种基于首字母大小写的可见性规则也适用于结构体字段和函数/方法。如果你不想允许外部访问,请使用小写字母。

英文:

If you want to force the factory function usage, name your struct (your class) with the first character in lowercase. Then, it won't be possible to instantiate directly the struct, the factory method will be required.

This visibility based on first character lower/upper case work also for struct field and for the function/method. If you don't want to allow external access, use lower case.

答案9

得分: 1

Golang在官方文档中并不是面向对象的语言。
Golang结构体的所有字段都有确定的值(不像C/C++),所以构造函数不像C++那样必要。
如果你需要给某些字段赋予特殊值,可以使用工厂函数。
Golang社区建议使用New..模式命名工厂函数。

英文:

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.

答案10

得分: 1

很多说法都说“在golang中没有默认构造函数”,但我们可以有以下这段代码,它可以编译和运行。我认为这就是一个Go初学者所称的默认构造函数。

package main

import (
	"fmt"
)

type MyInt int

type Person struct {
	Name string
	Age  int
}

func main() {
	a := MyInt(10)
	fmt.Println(a)

	p := Person{"Jack", 11}
	fmt.Println(p)
}
英文:

Many statements say "there's no default constructor in golang", but we can have this code, that compiles and runs. I think this is what a go novice would call default constructors

package main

import (
	&quot;fmt&quot;
)

type MyInt int

type Person struct {
	Name string
	Age  int
}

func main() {
	a := MyInt(10)
	fmt.Println(a)

	p := Person{&quot;Jack&quot;, 11}
	fmt.Println(p)
}

答案11

得分: -3

我刚开始学习Go语言。我有一个从其他语言中借鉴的模式,其中包含构造函数,并且可以在Go中使用。

  1. 创建一个init方法。
  2. init方法设为一次性例程(once routine)。它只在第一次调用时运行(每个对象一次)。
func (d *my_struct) Init() {
    //一次性例程
    if !d.is_inited {
        d.is_inited = true
        d.value1 = 7
        d.value2 = 6
    }
}
  1. 在该类的每个方法的顶部调用Init

这种模式在需要延迟初始化时也很有用(构造函数太早的情况)。

优点:它隐藏了类中的所有复杂性,客户端不需要做任何事情。

缺点:你必须记住在该类的每个方法的顶部调用Init

英文:

I am new to go. I have a pattern taken from other languages, that have constructors. And will work in go.

  1. Create an init method.
  2. Make the init method an (object) once routine. It only runs the first time it is called (per object).
func (d *my_struct) Init (){
    //once
    if !d.is_inited {
        d.is_inited = true
        d.value1 = 7
        d.value2 = 6
    }
}
  1. Call init at the top of every method of this class.

This pattern is also useful, when you need late initialisation (constructor is too early).

Advantages: it hides all the complexity in the class, clients don't need to do anything.

Disadvantages: you must remember to call Init at the top of every method of the class.

huangapple
  • 本文由 发表于 2013年8月8日 20:06:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/18125625.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定