英文:
Is there any tangible downside to using static type constructors in go?
问题
我一直觉得在Go语言中使用package.New()语法相当笨拙。
建议是,如果一个包只包含一个类型,使用package.New()来创建一个实例;如果存在多个类型,则使用package.NewBlah()。
http://golang.org/doc/effective_go.html#package-names
然而,如果你已经有一个具有New() API的现有包,并且要向该包添加一个新的外部类型,那么这种方法就会失败,因为你现在必须将其重命名为NewFoo()。现在你必须去改变使用New()的任何东西,这非常令人恼火。
...而且我对写下面这个代码的美感感到不满意:
import "other"
import "bar"
import "foo"
o := other.New() // <-- 奇怪,我得到的是什么类型?毫无头绪。
x := bar.New()
y := foo.NewFoo() // <-- 尴尬,使构造函数的命名看起来不一致
z := foo.NewBar()
所以,最近我改用了这种模式:
x := foo.Foo{}.New() // <-- 立即明显我得到的是一个Foo
y := foo.Bar{}.New() // <-- 只是在NewBar{}上额外增加了3个字符
o := other.Foo{}.New() // <-- 在所有包中保持一致,更新时不会破坏
模块的定义如下:
package foo
type Foo struct {
x int
}
func (s Foo) New() *Foo {
// 正常的初始化操作在这里
return &s // <-- 编辑:注意返回的是单个实例
}
type Bar struct {
}
func (Bar) New() *Bar {
return &Bar{} // <-- 编辑:不好,会导致双重分配。不要这样做。
}
Godoc似乎可以正常工作,并且对我来说更加明显和一致,没有额外的冗长。
所以,问题是:这种方法有什么明显的缺点吗?
英文:
I've always found the package.New() syntax in go rather awkward to work with.
The suggestion is that if a package holds only a single type, using package.New() to create an instance; if multiple types exist, using package.NewBlah().
http://golang.org/doc/effective_go.html#package-names
However, this approach falls down if you if you have an existing package with a New() api, adding a new external type to the package breaks the api, because you must now rename this NewFoo(). Now you have to go and change anything that uses New(), which is deeply irritating.
...and I'm just discontent with the aesthetic of writing this:
import "other"
import "bar"
import "foo"
o := other.New() // <-- Weird, what type am I getting? No idea.
x := bar.New()
y := foo.NewFoo() // <-- Awkward, makes constructor naming look inconsistent
z := foo.NewBar()
So, recently I've been using this pattern instead:
x := foo.Foo{}.New() // <-- Immediately obvious I'm getting a Foo
y := foo.Bar{}.New() // <-- Only an additional 3 characters on NewBar{}
o := other.Foo{}.New() // <-- Consistent across all packages, no breakage on update
Where the module is defined something like this:
package foo
type Foo struct {
x int
}
func (s Foo) New() *Foo {
// Normal init stuff here
return &s // <-- Edit: notice the single instance is returned
}
type Bar struct {
}
func (Bar) New() *Bar {
return &Bar{} // <-- Edit: Bad, results in double alloc. Not like this.
}
Godoc seems to work fine with it, and it seems more obvious and consistent to me, without additional verbosity.
So, question: Is there any tangible downside to this?
答案1
得分: 2
是的,它有一个缺点。这种方法可能会产生不必要的垃圾 - 这取决于特定Go编译器实现的优化程度。
英文:
Yes, it has a downside. This approach may generate unnecessary garbage - depending on how good the optimization of a specific Go compiler implementation is.
答案2
得分: 2
这不是非常惯用的写法,如果做得不好可能会产生过多的垃圾,正如你所指出的。实质上,你只是为你的对象创建了一个Init方法。我自己不常使用构造函数,更倾向于为我的对象设置有效的零值,只有在这不成立时才使用构造函数。
在你的情况下,我认为我会停止调用这个方法的名字为new,而是改为Init或Setup,以更好地反映它的功能。这样可以避免给人们关于它的功能产生错误的想法。
编辑:
我应该在这里更详细一些。将方法命名为Init或Setup,然后在零值上使用它,可以更好地向消费者说明正在发生的事情。例如:
f := &foo{}
f.Init()
这样可以避免产生过多的垃圾,并给你提供一个像你描述的初始化方法。
英文:
It's not terribly idiomatic and may if done badly create excess garbage as you note. Essentially you are just creating an Init method for your object. I don't use a lot of constructors myself tending to prefer having valid zero values for my objects and only using a constructor if that doesn't hold true.
In your case I think I'd just stop calling the method new and instead call it Init or Setup to better reflect what it's doing. That would avoid giving people the wrong idea about what it's doing.
Edit:
I should have been more detailed here. Calling the method Init or Setup and then using it on a Zero Value would better reflect what is going on to the consumer. eg
f := &foo{}
f.Init()
This avoids the excess garbage and gives you an initializer method as you describe.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论