Go中的构造函数

时间:2013-08-08 12:06:52

标签: oop constructor go

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

通常,这里要做的是使用构造函数,但由于go不是传统意义上的OOP,因此它们不是真正的对象,也没有构造函数。

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

如果不是Go中此类事物的最佳做法是什么?

11 个答案:

答案 0 :(得分:158)

当零值无法生成合理的默认值或者某些参数对于struct初始化是必需的时,有一些构造函数的等价物。

假设您有这样的结构:

type Thing struct {
    Name  string
    Num   int
}

然后,如果零值不合适,通常会构造一个返回指针的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}
}

参考:Allocation with new in Effective Go

答案 1 :(得分:105)

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

  1. 使结构的零值成为合理的默认值。 (虽然这对于来自“传统”oop的大多数人来说看起来很奇怪,但它通常很有效并且非常方便)。
  2. 提供一个函数func New() YourTyp,或者如果您的包函数func NewYourType1() YourType1中有多个这样的类型,依此类推。
  3. 记录你的类型的零值是否可用(在这种情况下,它必须由New...函数之一设置。(对于“传统主义”oops:没有读过的人文档将无法正确使用您的类型,即使他无法在未定义的状态下创建对象。)

答案 2 :(得分:30)

Go有对象。对象可以有构造函数(尽管不是自动构造函数)。最后,Go是一种OOP语言(数据类型附带了方法,但无可否认,OOP的定义是无穷无尽的。)

尽管如此,公认的最佳做法是为您的类型编写零个或多个构造函数。

由于@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

答案 3 :(得分:9)

Go中没有默认构造函数,但您可以为任何类型声明方法。你可以习惯于声明一个名为&#34; Init&#34;的方法。不确定这是否与最佳实践有关,但它有助于保持名称简短而不会失去清晰度。

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

答案 4 :(得分:5)

我喜欢这个blog post的解释:

  

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

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

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

答案 5 :(得分:3)

另一种方式是;

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

答案 6 :(得分:1)

如果要强制使用工厂功能,请用小写第一个字符命名结构(您的类)。然后,将无法直接实例化该struct,将需要factory方法。

基于第一个字符的小写/大写形式的可见性也适用于结构域和功能/方法。如果您不想允许外部访问,请使用小写字母。

答案 7 :(得分:1)

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

答案 8 :(得分:0)

Golang在其官方文件中不是OOP语言。 Golang结构的所有字段都有一个确定的值(不像c / c ++),因此构造函数不像cpp那样必要。 如果需要为某些字段指定一些特殊值,请使用工厂函数。 Golang的社区建议使用New ..模式名称。

答案 9 :(得分:-1)

如果您想模仿___.new()语法,您可以执行以下操作:

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

当然,遗憾的是Thing.new()如果没有CThing.new()也被实施(iirc)就无法实现,这有点让人感到羞耻......

答案 10 :(得分:-2)

我是新手。我从其他语言中提取了另一种模式,这些模式具有构造函数。并且可以随时使用。

  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