前往:禁止直接初始化结构

Ric*_*kyA 5 go

给定Go中的以下软件包,是否可以防止Barwith 的直接初始化而 Bar{..}不会Bar从软件包中暴露出来?

bar

package bar

import ()

type Bar struct {
    A string
    B string
}

func NewBar(baz string) Bar{
    return Bar{A:baz, B:baz+baz}
}
Run Code Online (Sandbox Code Playgroud)

main

package main

import (
    "fmt"

    "./bar"
)

func main() {
    x := bar.NewBar("sad") //all bars should be created with this
    y := bar.Bar{A: "fadss"} //and this should be disallowed
    bzzBar(x)
    bzzBar(y)
}

func bzzBar(bzz bar.Bar) { //but I can't do 'Bar -> bar' because I want to use the type
    fmt.Println(bzz)
}
Run Code Online (Sandbox Code Playgroud)

我的直觉说这无法完成,所以这也是一个有效的答案。

met*_*ule 5

没有办法阻止Bar{}Bar{A: "foo"}

要以您想要的方式控制结构,您可以返回一个接口而不是导出结构本身。

给出的例子:

package bar

type Bar interface{
    A() string
    B() string
    // if you need setters
    SetA(string)
    SetB(string)
}

type bar struct {
    a string
    b string
}

func (b *bar) A() string { return b.a }
func (b *bar) B() string { return b.b }

func (b *bar) SetA(val string) { b.a = val }
func (b *bar) SetB(val string) { b.b = val }

func NewBar(baz string) Bar {
    return &bar{a:baz, b:baz+baz}
}
Run Code Online (Sandbox Code Playgroud)

  • @Wessie 不要让你对 getter/setter 的盲目仇恨妨碍到这里。相信我,我和你一样不喜欢,但这是向用户隐藏结构初始化的惯用方法。它应该尽可能少地使用,但是如果用户不能用 `Bar{..}` 习语创建一个有意义的类型,它是非常有用的,因此你不会让他/她接触到它。 (5认同)
  • 无用的接口不是惯用的,这不是 java,你应该相信你的包的用户。如果只是代码更改需要现在新的 `NewBar` 而不是 `Bar{}`,您可以使用您的编辑器工具来查找和修复这些情况。 (4认同)

pet*_*rSO 4

Go标准库中使用的习惯用法是:

package bar

package bar

import (
    "fmt"
)

type Bar struct {
    a string
    b string
}

func New(baz string) *Bar {
    return &Bar{a: baz, b: baz + baz}
}

func (b *Bar) BzzBar() {
    fmt.Println(*b)
}
Run Code Online (Sandbox Code Playgroud)

package main

package main

import (
    "bar"
)

func main() {
    x := bar.New("sad") //all bars should be created with this
    x.BzzBar()
    // error: unknown bar.Bar field 'A' in struct literal
    // y := bar.Bar{A: "fadss"} //and this should be disallowed
}
Run Code Online (Sandbox Code Playgroud)

输出:

{sad sadsad}
Run Code Online (Sandbox Code Playgroud)

附录:

Go 编程语言规范

零值

当通过声明或调用 make 或 new 分配内存来存储值并且未提供显式初始化时,将为内存提供默认初始化。此类值的每个元素都设置为其类型的零值:对于布尔值为 false,对于整数为 0,对于浮点数为 0.0,对于字符串为 "",对于指针、函数、接口、切片、通道和映射为 nil。此初始化是递归完成的,因此,例如,如果未指定值,则结构数组的每个元素都将其字段归零。

Go 标准库中使用的另一个习惯用法是使零值有意义。例如,如果new尚未显式初始化,它将具有零默认值false

type Bar struct {
    new bool
    a   string
    b   string
}
Run Code Online (Sandbox Code Playgroud)

例如,

package bar

import (
    "fmt"
)

type Bar struct {
    new bool
    a   string
    b   string
}

func New(baz string) *Bar {
    return &Bar{new: true, a: baz, b: baz + baz}
}

func (b *Bar) notnew() {
    if b == nil || !b.new {
        panic("bar.Bar not bar.New")
    }
}

func (b *Bar) Bzz() {
    b.notnew()
    fmt.Println(*b)
}
Run Code Online (Sandbox Code Playgroud)

package main

import (
    "bar"
)

func main() {
    x := bar.New("sad") //all bars should be created with this
    x.Bzz()
    // error: unknown bar.Bar field 'A' in struct literal
    // y := bar.Bar{A: "fadss"} //and this should be disallowed

    // var b bar.Bar
    // panic: bar.Bar not bar.New
    // b.Bzz()

    // var b = bar.Bar{}
    // panic: bar.Bar not bar.New
    // b.Bzz()

    // var bp *bar.Bar
    // panic: bar.Bar not bar.New
    // bp.Bzz()

    // var bp = new(bar.Bar)
    // panic: bar.Bar not bar.New
    // bp.Bzz()
}
Run Code Online (Sandbox Code Playgroud)

输出:

{true sad sadsad}
Run Code Online (Sandbox Code Playgroud)

  • 嗯;是的,这是标准的围棋习惯用法;是的,它可以隐藏字段;但 `y := Bar{}` 仍然愉快地进行着。我想真正的答案是:忍受它。 (2认同)