How to write clean Go?

blov · 2017-08-20 17:00:04 · 728 次点击    
这是一个分享于 2017-08-20 17:00:04 的资源,其中的信息可能已经有所发展或是发生改变。

I started playing around with Go about a week ago and I am having a lot of difficulty with producing code that I am happy with. I am a C# developer by trade and have dabbled with various other languages, but Go's lack of traditional OOP is throwing me for a loop.

That being said, I have a few questions:

First, are any articles or anything you suggest I read?

Second, do any of you have a backend web service you've built that is on Github or something? I think it would be very beneficial to see some patterns other users are using.

Third, can you take a look at some basic code I have put together and steer me in the right (or just better) direction?

The actual functionality of my code isn't important. There is a main package that registers the AccountRegister function, but it isn't included.

https://gist.github.com/anonymous/50ab2748ced1cc8ac22c1d33161f0581

Thanks for all your help.


评论:

BraveNewCurrency:

I started playing around with Go about a week ago and I am having a lot of difficulty with producing code that I am happy with.

Go is not a toy language. Expect to spend more than a week to really understand it.

Go's lack of traditional OOP is throwing me for a loop.

It's actually OOP that is throwing you for a loop. Because of inheritance, it's rare that you can copy a class (for a domain object) from one project to the next. With Go, you compose your objects (structs) from isolated sub-objects (structs). It's almost like OOP mix-ins, except go enforces the rule "sub-objects don't know about the larger objects they are embedded in". At first, this seems like a serious limitation. But later, you realize that it forces modular code, and allows really neat re-use. For example, the "Mutex" struct can't know anything about the object it's locking, therefore it can be re-used anywhere.

nstratos:

First, are any articles or anything you suggest I read?

Feel free to check out this comment which has a collection of some good articles that help with structuring code in a Go project.

Third, can you take a look at some basic code I have put together and steer me in the right (or just better) direction?

About your code I think it looks okay.

Something you could improve is to do some better error handling. You could use the technique described in this article and keep the error handling of all your handlers in one place.

const (
    PasswordMismatch = "PasswordMismatch"
)

If you think you'll add more errors later you could use iota like this.

const Debug = true

Another technique you could instead of a debug const is build tags. You could have a function with empty implementation in your normal files and guard that file with // +build !debug and have another file with // +build debug which will contain the debug implementation. Then when you compile with go build -tags=debug the go toolchain will use the debug implementation.

You can see this technique being used in the Upspin project. Check the debug.go, debug_stub.go and errors.go files. I think it is also a very good project to learn from.

And you can use build tags for all sorts of stuff like for example writing integration tests which since they might have to use a database or some other system you don't want them to run every type you do go test so you use a build tag and run them only with something like go test -tags=db.

titpetric:
  1. You're welcome to read my blog for some focused articles, https://scene-si.org, Go by example is a great all-round reference: https://gobyexample.com/ - and look at examples in the stdlib documentation, godoc for packages. If it's a back-end service, I'd suggest to get familiar with database/sql and specifically jmoiron/sqlx for MySQL., - just consume anything that's relevant to you,
  2. For a front/backend service, you can take a look at my titpetric/pendulum, it provides a simple web api for a front-end VueJS app (file browser + static file editor).

In regards to your example code, I would bind the functions around a struct, ie, Account, func NewAccount() Account, and handle complete API endpoints for accounts here. In the example project above I do the same, albeit I just use a generic "API" name to group individual endpoints. While not a requirement, I prefer this over creating a package. It's sort of a trade off.

If you have a small project, grouping functions around a struct is good. If you have a big project, you'll move onto packages for cleaner separation. If you have a small project (microservice), you might implement a flat structure for endpoints, like you did. If this project grows - you might want to separate the http interface away from the actual "native" api. I do this in the Pendulum API above, calls like StoreHandler invoke a more native API Store hidden behind it. It separates the HTTP logic and response structures away from the more internal API which could be used as a package import.

You'll learn the most by writing a lot of code. The above example was written in about 4 hours, and with such time constraints I'm sure it's better than if I had a very long time to write it (of course, also not perfect but I only have minor thoughts about how I would more strictly separate the HTTP API and the internal API).

Keep on coding :)


入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

728 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传