Go中的构造函数

我有一个结构,我想用一些合理的默认值初始化它。

通常,这里要做的事情是使用构造函数,但由于go不是传统意义上的真正面向对象,所以它们不是真正的对象,而且它没有构造函数。

我注意到了init方法,但那是在包级别。是否有其他类似的东西可以在结构层使用?

如果不是,那么在围棋中这类事情的公认最佳实践是什么?

190419 次浏览

当0值不能作为合理的默认值时,或者当结构初始化需要一些形参时,有一些构造函数的等价函数。

假设你有一个这样的结构:

type Thing struct {
Name  string
Num   int
}

那么,如果0值不合适,你通常会构造一个NewThing函数返回指针的实例:

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

当你的结构体足够简单时,你可以使用这个压缩结构:

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

如果你不想返回指针,那么一种做法是调用函数makeThing而不是NewThing:

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

参考:分配与新的有效Go

实际上有两种公认的最佳实践:

  1. 将结构的0值设置为合理的默认值。(虽然这对大多数来自“传统”的人来说看起来很奇怪,但它通常是有效的,而且真的很方便)。
  2. 提供一个函数func New() YourTyp,或者如果你的包中有几个这样的类型函数func NewYourType1() YourType1,等等。

记录你的类型的零值是否可用(在这种情况下,它必须由New...函数之一设置。(对于“传统主义者”来说:不阅读文档的人将无法正确使用您的类型,即使他不能在未定义状态下创建对象。)

Go有对象。对象可以有构造函数(尽管不是自动构造函数)。最后,Go是一种面向对象语言(数据类型有附加的方法,但不可否认,关于什么是面向对象有无数的定义)。

然而,公认的最佳实践是为类型编写零个或多个构造函数。

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

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

我想给你们看这个版本的原因是,很多时候“内联”;字面量可以用来代替“构造词”。调用。

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

现在*a == *b

Golang在其官方文件中不是面向对象语言。 Golang struct的所有字段都有一个确定的值(不像c/c++),所以构造函数不像cpp那么需要。 如果需要为某些字段分配一些特殊值,请使用工厂函数。 Golang的社区建议新…模式名称。< / p >

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

另一种方法是;

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

我喜欢这个博客的解释:

函数New是用于为应用程序开发人员创建核心类型或不同类型的包的Go约定。看看New是如何在log中定义和实现的。去,bufio。Go和cypto.go:

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

crypt .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")
}

由于每个包都充当一个名称空间,因此每个包都可以有自己的New版本。在bufio。go可以创建多种类型,因此没有独立的New函数。在这里你会发现NewReader和NewWriter这样的功能。

如果你想强制使用工厂函数,命名你的结构(你的类)用小写的第一个字符。然后,它将不可能直接实例化结构,将需要工厂方法。

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

在Go中,构造函数可以使用返回已修改结构的指针的函数来实现。

type Colors struct {
R   byte
G   byte
B   byte
}


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

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

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

我是新来的。我有一个模式从其他语言,有构造函数。并将继续工作。

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

当您需要后期初始化(构造函数太早)时,这种模式也很有用。

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

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