<p>I'm working on a webapp in Go and I'd like to have a unified interface for frontend, controller, and storage. To that end, I have a Post type which has ID, UserID, Title, and Text. However, when creating the post, there's no way for any layer except for the storage layer to know what the Post's ID should be. What is the idiomatic way to represent an optional value like this?</p>
<hr/>**评论:**<br/><br/>jerf: <pre><p>Based on your description, it is very likely you can use composition:</p>
<pre><code>type PostContents struct {
UserID UserID `json:"user_id"`
Title string `json:"title"`
Text string `json:"text"`
}
type Post struct {
PostID int `json:"id"`
PostContents
}
</code></pre>
<p>The reason I say this is that it is likely that if you exercise type discipline consistently you will find that at any given point in the program, you will <em>definitively</em> have either a <code>PostContents</code> or a <code>Post</code>. If you are careful with your types, you are likely to find there is never a point where you have "maybe a post with an ID, maybe a post without an id". Though it does sometimes take a bit of practice to get good enough with typed programming to be correct enough in your handling to get this correct.</p>
<p>The next tier of flexibility is interfaces. If you <em>do</em> have a place where you may be manipulating "a post that may or may not have an ID", you can declare an interface:</p>
<pre><code>type PostContentsI interface {
GetUserID() UserID
GetTitle() string
GetText() string
}
</code></pre>
<p>and implement it on <code>PostContents</code>. By the way composition works, that will automatically implement it on the <code>Post</code> I show above, so <code>Post</code> will automatically conform to <code>PostContentsI</code>. This is a bit smellier than the above, but is still statically-typed and has compile-time guarantees that anything using <code>PostContentsI</code> will still not manipulate the ID that may or may not exist. This is especially true if the value being passed by interface doesn't really "go" anywhere else after that; for instance, one thing I would accept without too much thought in this context would be a <code>Render(p PostContentsI, w io.Writer)</code> method since it is completely plausible to me that you may want to render a post prior to it being given an ID. (For instance, a live preview function in the post creation UI.)</p>
<p>(I don't love the name PostContentsI, but without more context I don't know what you might want to call this. RenderablePost would be plausible if that's your primary use for the interface, for instance. Which you'll probably find evolving into a <code>Renderable</code> on you....)</p>
<p>Only as a last resort would I consider using a pointer to the ID that may or may not be nil, in a <code>Post</code> struct that tries to encompass both cases. That puts all the onus on you to ensure that every time you go to use an ID, you must first check that it exists. Better to let the compiler do that with the types above.</p>
<p>There isn't <em>that</em> much daylight between an expert Go programmer and a beginner in my opinion, at least not compared to most languages, but fluidity with the compositional style rather than the inheritance style is definitely one of the distinctions between beginner and expert.</p></pre>SilverWingedSeraph: <pre><p>Thank you very much for this in-depth response! It's exactly what I was looking for.</p></pre>itsmontoya: <pre><p>If your payload is json, you can setup omitempty fields </p></pre>throwawayguy123xd: <pre><p>you make an interface, and then your type can have functions</p>
<p><code>func (post p) getID() int { return p.id }</code></p></pre>egonelbre: <pre><pre><code>package post
type ID int64
type Post struct {
ID ID `json:"id,omitempty"`
Owner user.ID `json:"owner"`
Title string `json:"title"`
Text string `json:"text"`
}
func (p *Post) IsStored() bool { return p.ID == 0 }
</code></pre></pre>notsoobadusername: <pre><p>Quick question: Why not:</p>
<pre><code>ID int64 `json:"id,omitempty"`
</code></pre></pre>egonelbre: <pre><p>Usually you end up with a lot of different IDs, which can be easily mixed up. So just some typesafety at the cost of having to deal with explicit casts some times.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传