Which one is more idiomatic code? (receiver or not)

agolangf · · 453 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;m faily new to go and was wondering which one of these is more idomatic. It just wanted to build a simple CRUD application using <a href="https://github.com/jmoiron/sqlx" rel="nofollow">sqlx</a>.</p> <pre><code>type Project struct { Id int `db:&#34;id&#34;` Title string `db:&#34;title&#34;` } </code></pre> <p>Option 1:</p> <pre><code>func (p *Project) Get(id int) error { return db.Get(p, &#34;SELECT * FROM projects WHERE id = $1&#34;, id) } p := Project{} p.Get(1) </code></pre> <p>Option 2:</p> <pre><code>func GetProject(id int) (Project, error) { p := Project{} err := db.Get(&amp;p, &#34;SELECT * FROM projects WHERE id = $1&#34;, id) return p, err } p, _ := GetProject(1) </code></pre> <p>I assume something like this would be alright but I wasn&#39;t sure about the one above:</p> <pre><code>p := Project{Title: &#34;Project&#34;} p.Save() // Writes the newly created Project to the database </code></pre> <p>Thanks in advance :)</p> <hr/>**评论:**<br/><br/>daveddev: <pre><p>Instead of a global, try wrapping the db and add methods to that struct.</p> <pre><code>type RealDB struct { *sqlx.DB } func (db *RealDB) ProjectByID(id int) (*Project, error) </code></pre> <p>Create an interface with the related methods.</p> <pre><code>type MainDB interface { ProjectByID(int) (*Project, error) } </code></pre> <p>The db can then be mocked for testing by implementing the interface.</p> <pre><code>func (db *MockDB) ProjectByID(id int) (*Project, error) { return &amp;Project{ID: id}, nil } </code></pre> <p><a href="http://www.alexedwards.net/blog/organising-database-access" rel="nofollow">http://www.alexedwards.net/blog/organising-database-access</a></p></pre>flippeeer: <pre><p>Thank you, that was very helpful. </p></pre>tv64738: <pre><blockquote> <p>func (db <em>sqlx.DB) ProjectByID(id int) (</em>Project, error)</p> </blockquote> <p>That ain&#39;t gonna work. You need to define a type to add methods to it.</p></pre>daveddev: <pre><p>I spotted that and edited the example just before you replied. Good catch, though.</p></pre>divan0: <pre><p>Basically, you&#39;re questioning &#39;methods vs functions&#39; thing. I believe, it was well covered by Steve Francia (@spf13) in his talk &#34;7 common mistakes in Go&#34;. <a href="http://spf13.com/presentation/7-common-mistakes-in-go-2015/" rel="nofollow">http://spf13.com/presentation/7-common-mistakes-in-go-2015/</a></p> <p>To be short, if you simply need to populate Project structure with data from the external storage - you don&#39;t really have a state. It&#39;s just data moving from outside to your variable. Use function (it&#39;s option 2 in your example)</p> <p>Note, that &#39;function&#39; in its turn, can be a method of another type. For example:</p> <pre><code>project, err := db.Project(1) </code></pre></pre>aaaqqq: <pre><p>Not a Go expert &amp; not sure which one&#39;s more idiomatic, but I&#39;d prefer option 2 as it is more readable and could be used without first declaring a project instance.</p></pre>dlsniper: <pre><p>Option 2 would be better because then the concerns of each functionality are separated. It looks like Project is used a model and I would expect another piece of the application to handle the lifecycle of it.</p> <p>Same goes for the save functionality, Save(project *Project) error</p> <p>It&#39;s also worth noting that in Go it&#39;s preferred to say Get and Save and omit the &#34;target&#34; (Project)</p></pre>flippeeer: <pre><p>Thanks for your answer. Should I create an interface and let all my models implement the Get and Save method to &#34;omit the target&#34;? Or do you have another idea how that could be done?</p></pre>

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

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