How to write clean Go?

blov · · 561 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>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&#39;s lack of traditional OOP is throwing me for a loop.</p> <p>That being said, I have a few questions:</p> <p>First, are any articles or anything you suggest I read? </p> <p>Second, do any of you have a backend web service you&#39;ve built that is on Github or something? I think it would be very beneficial to see some patterns other users are using. </p> <p>Third, can you take a look at some basic code I have put together and steer me in the right (or just better) direction?</p> <p>The actual functionality of my code isn&#39;t important. There is a main package that registers the AccountRegister function, but it isn&#39;t included.</p> <p><a href="https://gist.github.com/anonymous/50ab2748ced1cc8ac22c1d33161f0581" rel="nofollow">https://gist.github.com/anonymous/50ab2748ced1cc8ac22c1d33161f0581</a></p> <p>Thanks for all your help.</p> <hr/>**评论:**<br/><br/>BraveNewCurrency: <pre><blockquote> <p>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.</p> </blockquote> <p>Go is not a toy language. Expect to spend more than a week to really understand it.</p> <blockquote> <p>Go&#39;s lack of traditional OOP is throwing me for a loop.</p> </blockquote> <p>It&#39;s actually OOP that is throwing you for a loop. Because of inheritance, it&#39;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&#39;s almost like OOP mix-ins, except go enforces the rule &#34;sub-objects don&#39;t know about the larger objects they are embedded in&#34;. 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 &#34;Mutex&#34; struct can&#39;t know anything about the object it&#39;s locking, therefore it can be re-used anywhere.</p></pre>nstratos: <pre><blockquote> <p>First, are any articles or anything you suggest I read?</p> </blockquote> <p>Feel free to check out <a href="https://www.reddit.com/r/golang/comments/56wk4v/structuring_go_api/d8n4axl/" rel="nofollow">this comment</a> which has a collection of some good articles that help with structuring code in a Go project.</p> <blockquote> <p>Third, can you take a look at some basic code I have put together and steer me in the right (or just better) direction?</p> </blockquote> <p>About your code I think it looks okay. </p> <p>Something you could improve is to do some better error handling. You could use the technique described in <a href="https://blog.golang.org/error-handling-and-go#TOC_3." rel="nofollow">this article</a> and keep the error handling of all your handlers in one place.</p> <blockquote> <pre><code>const ( PasswordMismatch = &#34;PasswordMismatch&#34; ) </code></pre> </blockquote> <p>If you think you&#39;ll add more errors later you could use <code>iota</code> like <a href="https://github.com/upspin/upspin/blob/master/errors/errors.go#L68-L117" rel="nofollow">this</a>.</p> <blockquote> <p><code>const Debug = true</code></p> </blockquote> <p>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 <code>// +build !debug</code> and have another file with <code>// +build debug</code> which will contain the debug implementation. Then when you compile with <code>go build -tags=debug</code> the go toolchain will use the debug implementation.</p> <p>You can see this technique being used in the <a href="https://github.com/upspin/upspin/tree/master/errors" rel="nofollow">Upspin</a> project. Check the <code>debug.go</code>, <code>debug_stub.go</code> and <code>errors.go</code> files. I think it is also a very good project to learn from.</p> <p>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&#39;t want them to run every type you do <code>go test</code> so you use a build tag and run them only with something like <code>go test -tags=db</code>.</p></pre>titpetric: <pre><ol> <li>You&#39;re welcome to read my blog for some focused articles, <a href="https://scene-si.org" rel="nofollow">https://scene-si.org</a>, Go by example is a great all-round reference: <a href="https://gobyexample.com/" rel="nofollow">https://gobyexample.com/</a> - and look at examples in the stdlib documentation, godoc for packages. If it&#39;s a back-end service, I&#39;d suggest to get familiar with database/sql and specifically jmoiron/sqlx for MySQL., - just consume anything that&#39;s relevant to you,</li> <li>For a front/backend service, you can take a look at my <a href="https://github.com/titpetric/pendulum" rel="nofollow">titpetric/pendulum</a>, it provides a simple web api for a front-end VueJS app (file browser + static file editor).</li> </ol> <p>In regards to your example code, I would bind the functions around a struct, ie, <code>Account</code>, <code>func NewAccount() Account</code>, and handle complete API endpoints for accounts here. In the example project above I do the same, albeit I just use a generic &#34;API&#34; name to group individual endpoints. While not a requirement, I prefer this over creating a package. It&#39;s sort of a trade off.</p> <p>If you have a small project, grouping functions around a struct is good. If you have a big project, you&#39;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 &#34;native&#34; api. I do this in the Pendulum API above, calls like <code>StoreHandler</code> invoke a more native API <code>Store</code> 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.</p> <p>You&#39;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&#39;m sure it&#39;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).</p> <p>Keep on coding :)</p></pre>

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

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