douchao1879 2014-04-02 11:10
浏览 54
已采纳

简短的变量声明会导致Go中的代码结构不良吗? [关闭]

From looking at a lot of Go code on GitHub, I have noticed that Go coders love the short variable declaration (:=)and use it very often. Here's an example Coding Style. But this usage seems far too often to create poorly structured code: Very long functions that bundle lots of functionality together, because Short variable declarations may appear only inside functions. If you want to set up a package that encapsulates something akin to a class with members that several short, modularized functions operate on, as good structured programming and OOP practices mandate, you can't really use short variable declarations effectively for member variables. I tend to get uneasy whenever I see any function that's more than 10 or 15 lines long - I know something's probably not right with that design.

Personally, I'm not a big fan of short variable declarations except for local initializion of loop counters, etc. Aside from the above mentioned issue, I like to see clearly the type I'm working with. Particularly when looking over new code, short variable declarations assume that the reader knows what the function initializing the variable is returning, or obliges them to go and find out, or deduce it from the context. So, that code becomes less readable and requires the reader to stop, and perhaps search somewhere for its meaning, whereas avardeclaration might make things immediately clear.

(I suppose one way write better code and still use short variable declarations would be to avoid the use of package global members entirely and parameterize all your functions - not necessarily a bad thing - but this probably creates more work than you will save using short variable declarations.)

As a result I have been opting to use this sort of design for my packages, similar to the way declaration and initialization work in traditional OOP languages such as Delphi and 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)

}

Then I have clearly typed, initialized and encapsulated package members that are available for use in the package. Yes, this does violate the good practice of initializing only when necessary, but I don't have to do this in init() either - can do it on an as needed basis, when the member is used for the first time, although that has other potential complications. (Be that as it may, since initialization of class members in a constructor has been common practice for a long time, I don't have much of problem with this, regardless.)

Is this non-idiomatic, "bad" code in Go? Is the abundant use of short variable declarations, with their IMO negative consequences, considered a good thing? Frankly I fail to see how it could be. I tend to think that perhaps short variable declarations are being used too much by programmers who just love the short syntax, and the result is a lot of bloated looking spaghetti style code. Am I missing something?


Edit: Since the question as stated caused a good deal of confusion, I'll try to illustrate with a simple example (this may or may not compile - wrote quickly just to illustrate)

If I write:

package main

import
(
"packageA"
"packageB"
)


func DoStuff(){

    a:=PackageA.T
    a.Dostuff()

    }

Then it will be very easy to continue and write:

func doStuff(){

    a:=PackageA.T
    b:=PackageB.T
    Dostuff(a)
    DoMorestuff(b)
    DoEvenMorestuff(a,b)
    DoLotsofstuff(a,b)

    .....


    }

func main(){
  DoStuff()
}

IMO bundled, bloated, poorly structured code.

______________________

But when I write

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()

}

A modularized pipeline style design, which cannot be implemented with the short variable declaration form. The best that can be done using the short form is nested, parameterized functions, which are generally not a very good design choice either.

Some complained that this amounts to globals, but in a well designed, encapsulated package with a minimal public interface, that is no more of an issue than declaring variables local to a function. Packages should be atomic. Member variables have been an accepted component of OOP design "forever" and when used properly, following the rules of OOP and structured programming, they are not globals, but locals to the package or module or class which encapsulates them.

Granted, there is no feature of any language that cannot be used, or abused. My question is simply that short variable declarations seem to be ripe for abuse and force certain design decisions that are less than desirable, unless used very discreetly. I'm asking if there is a way to use the form that will circumvent the issues I have with them and afford me their ease of use without the drawbacks.

Edit 2:


Perhaps this is something of a compromise:

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()

}

Still bundled up in main() but that's not really a complaint - doStackofStuff() is my interface call. In "real code" I would write a separate package for all of it, and only DoStackofStuff() would be public and callable from main() - the rest would be encapsulated. The implementation is broken up in doStackofStuff(), yet using the short form without nesting.

  • 写回答

2条回答 默认 最新

  • dongquanyu5816 2014-04-02 12:04
    关注

    I think you are mixing a few issues here that are not connected:

    1. If you need to emulate classes and structs in Go - don't use modules for them. Use structs. Build "constructors" for them. That's it. I'd hardly even call it emulation, even though it's not 100% identical to C++ or Java classes. I mean, why not just do something like

      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)
      }
      
    2. I don't fully see why doing := inside functions will create spaghetti code. Can you make your point clearer on that? The most harm it can do is scope collisions if you're not careful - like in this example:

      var x int = 3
      
      func main() {
          if x == 3 {
              x := 5
              fmt.Println(x) // will print 5
          }
      
          fmt.Println(x) //will print 3
      
      }
      
    3. going back to your example - it's not bad design to import types from a different module (e.g. have a static http client initiated in a module's init() function). But you have to make sure you are really not mixing up responsibility between the two packages.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记