Coding style 短变量声明是否会导致Go中的代码结构不良?
通过查看GitHub上的大量Go代码,我注意到Go程序员喜欢短变量声明(Coding style 短变量声明是否会导致Go中的代码结构不良?,coding-style,go,initialization,declaration,packages,Coding Style,Go,Initialization,Declaration,Packages,通过查看GitHub上的大量Go代码,我注意到Go程序员喜欢短变量声明(:=),并且经常使用它。这里有一个例子。但这种用法似乎太过频繁,无法创建结构不良的代码:非常长的函数将许多功能捆绑在一起,因为如果您想按照良好的结构化编程和OOP实践的要求,设置一个封装类似于类的东西的包,包中包含几个短的模块化函数操作的成员,对于成员变量,不能真正有效地使用短变量声明。每当我看到任何长度超过10或15行的函数时,我都会感到不安——我知道这种设计可能有问题 就个人而言,除了循环计数器的局部初始化等,我不太喜欢
:=
),并且经常使用它。这里有一个例子。但这种用法似乎太过频繁,无法创建结构不良的代码:非常长的函数将许多功能捆绑在一起,因为如果您想按照良好的结构化编程和OOP实践的要求,设置一个封装类似于类的东西的包,包中包含几个短的模块化函数操作的成员,对于成员变量,不能真正有效地使用短变量声明。每当我看到任何长度超过10或15行的函数时,我都会感到不安——我知道这种设计可能有问题
就个人而言,除了循环计数器的局部初始化等,我不太喜欢短变量声明。除了上面提到的问题,我喜欢清楚地看到我正在使用的类型。特别是在查看新代码时,简短的变量声明假定读者知道初始化变量的函数返回的是什么,或者要求他们去查找,或者从上下文中推断。因此,代码变得不那么可读,需要读者停下来,也许在某个地方搜索它的含义,而var
声明可能会使事情立即变得清楚
(我认为编写更好的代码并仍然使用短变量声明的一种方法是完全避免使用包全局成员并参数化所有函数——这不一定是一件坏事——但这可能会比使用短变量声明节省更多的工作。)
因此,我一直选择在我的包中使用这种设计,类似于在传统的面向对象语言(如Delphi和C++)中声明和初始化的工作方式:
package myPackage
import (
"importA"
"importB"
)
var m_member1 *importA.T
var m_member2 *importB.T
func init() {
m_member1 = new(importA.T)
m_member2 = new(importB.T)
}
然后,我清楚地键入、初始化和封装了可在包中使用的包成员。是的,这确实违反了只有在必要时才初始化的良好做法,但我也不必在init()中这样做-可以在第一次使用成员时根据需要这样做,尽管这有其他潜在的复杂性。(尽管如此,由于在构造函数中初始化类成员已经是很长一段时间的常见做法,无论如何,我对此没有太多问题。)
这是围棋中非惯用的“坏”代码吗?大量使用短变量声明及其消极后果被认为是一件好事吗?坦率地说,我不明白这是怎么回事。我倾向于认为,那些喜欢短语法的程序员可能使用了太多的短变量声明,其结果是产生了大量看起来臃肿的意大利面条式代码。我错过什么了吗
编辑:由于前面提到的问题造成了大量的混乱,我将尝试用一个简单的例子来说明(这可能是编译,也可能不是编译-快速编写只是为了说明)
如果我写:
package main
import
(
"packageA"
"packageB"
)
func DoStuff(){
a:=PackageA.T
a.Dostuff()
}
这样就很容易继续写:
func doStuff(){
a:=PackageA.T
b:=PackageB.T
Dostuff(a)
DoMorestuff(b)
DoEvenMorestuff(a,b)
DoLotsofstuff(a,b)
.....
}
func main(){
DoStuff()
}
IMO捆绑、臃肿、结构不良的代码
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
但是当我写作的时候
package main
import
( "packageA"
"packageB"
)
var a packageA.T
var b packageB.T
init(){
a=new(packageA.T)
b=new(packageB.T)
}
Then I can write:
func dostuff(){
a.Write()
}
func doMorestuff(){
b.Write()
}
func doEvenMorestuff(){
a.Read(b)
}
func doLotsofstuff(){
a.ReadWrite(a,b)
b.WriteRead(b,a)
}
func main(){
dostuff()
doMorestuff()
doEvenMorestuff()
doLotsofstuff()
}
模块化管道样式设计,不能用短变量声明形式实现。使用简短形式可以做的最好的事情是嵌套的、参数化的函数,这通常也不是一个很好的设计选择
一些人抱怨说这相当于全局变量,但在一个设计良好、封装良好、具有最小公共接口的包中,这与声明函数的局部变量相比并不是什么问题。包应该是原子的。成员变量“永远”是OOP设计中被接受的组件,如果使用得当,遵循OOP和结构化编程的规则,它们不是全局变量,而是封装它们的包、模块或类的局部变量
诚然,任何语言都没有不能使用或滥用的特征。我的问题很简单,除非非常谨慎地使用,否则短变量声明似乎已经成熟,可以被滥用,并强制执行某些不太理想的设计决策。我想问的是,是否有一种方法可以使用该表单来规避我与他们之间的问题,并让我在没有缺点的情况下轻松地使用它们
编辑2:
package main
import
(
"packageA"
"packageB"
)
func dostuff(a PackageA.T){
a.Write()
}
func doMorestuff( b PackageB.T ){
b.Write()
}
func doEvenMorestuff(a a PackageA.T, b PackageB.T ){
a.Read(b)
}
func doLotsofstuff(a a PackageA.T, b PackageB.T ){
a.ReadWrite(a,b)
b.WriteRead(b,a)
}
func doStackofStuff(){
a:=PackageA.T
b:=PackageB.T
dostuff(a)
doMorestuff(b)
doEvenMorestuff(a,b)
doLotsofstuff(a,b)
}
func main(){
doStackofStuff()
也许这是一种妥协:
package main
import
(
"packageA"
"packageB"
)
func dostuff(a PackageA.T){
a.Write()
}
func doMorestuff( b PackageB.T ){
b.Write()
}
func doEvenMorestuff(a a PackageA.T, b PackageB.T ){
a.Read(b)
}
func doLotsofstuff(a a PackageA.T, b PackageB.T ){
a.ReadWrite(a,b)
b.WriteRead(b,a)
}
func doStackofStuff(){
a:=PackageA.T
b:=PackageB.T
dostuff(a)
doMorestuff(b)
doEvenMorestuff(a,b)
doLotsofstuff(a,b)
}
func main(){
doStackofStuff()
}
仍然捆绑在main()
中,但这不是真正的抱怨-doStackofStuff()
是我的接口调用。在“真实代码”中,我将为所有代码编写一个单独的包,只有DoStackofStuff()
是公共的,并且可以从main()
调用,其余的将被封装。该实现在dostackOfsuff()
中被分解,但是使用了没有嵌套的简短形式。我认为您在这里混合了一些没有关联的问题:
如果您需要在Go中模拟类和结构,不要为它们使用模块。使用结构。为他们构建“构造函数”。就这样。我甚至不称它为仿真,即使它不是100%与C++或java类相同。我是说,为什么不做点像
type Foo struct {
Bar string
Baz int
}
func NewFoo(bar string, baz int) *Foo {
return &Foo{
bar,
baz,
}
}
//and if you want static state - just do this
var DefaultFoo *Foo
func init() {
DefaultFoo = NewFoo("foo", 1)
}
我不完全明白为什么在函数内部执行:=
会创建意大利面代码。你能把你的观点说得更清楚些吗?如果不小心,它可能造成的最大危害是范围冲突-如本例中所示:
var x int = 3
func main() {
if x == 3 {
x := 5
fmt.Println(x) // will print 5
}
fmt.Println(x) //will print 3
}
回到您的示例—从不同模块导入类型(例如,在模块的init()函数中启动静态http客户机)的设计不错。但你必须确保你真的没有混淆两个包之间的责任
答案其实很简单。短变量声明(例如a:=2)的唯一替代方法是