<p>I use two (standard) phrases/prompts to help me think about interface design. What do others use?</p>
<ul>
<li><p>“do-er” eg stdlib io.Reader (Read), io.Writer (Write). If thinking about “humans” (child, student, tourist), I might start with Walker (Walk), Talker (Talk), allowing methods VisitPark("Centennial"), Phone("John")</p></li>
<li><p>“has-a”. I'm more careful here, because "has-a" can lead to thinking of "properties" like OO design. But "has-a" credit card, implies Pay() therefore interface Payer, allowing interface methods like PurchaseOn("ebay"). Or "has-a" car implies Drive(), therefore interface Driver, allowing methods TransportPassengers(4), TransportFurniture("sofa")</p></li>
</ul>
<p>Many Golang interface articles focus on implementation, which is straight forward once you've learnt it, but design is harder. Some better articles:</p>
<ul>
<li><a href="http://www.golangprograms.com/golang/interface-type/" rel="nofollow">http://www.golangprograms.com/golang/interface-type/</a></li>
<li><a href="https://stackoverflow.com/questions/38842457/interface-naming-convention-golang" rel="nofollow">https://stackoverflow.com/questions/38842457/interface-naming-convention-golang</a></li>
</ul>
<p>Any other good articles?</p>
<hr/>**评论:**<br/><br/>DeedleFake: <pre><p>I usually think of it the other way around. I try to design interfaces based on what I need to use them for, not based on existing functionality.</p>
<p>For example, I've got <a href="https://www.github.com/DeedleFake/sirdsc" rel="nofollow">a little library for generating autostereograms</a> that I recently overhauled after not touching the code in about six years. When I did, I was thinking about the input depth map for the main <code>Generate()</code> function that the library provides. At the time, the function took an <code>image.Image</code> to use as a depth map, and it then calculated depths from the pixel values. I realized that I actually didn't need that information; all I really needed it to do was provide a way to get the depth information. So I made a new interface and changed the argument type to that:</p>
<pre><code>type DepthMap interface {
Bounds() image.Rectangle
Depth(x, y int) int
}
</code></pre>
<p>I then created a new intermediary type that wraps an <code>image.Image</code> and provides a <code>Depth()</code> method that does the pixel-value-to-depth conversion on the fly.</p>
<p>In general, one of the things that I really like about Go's interface system is that it essentially allows duck typing in a statically typed language. It's basically a less powerful, but also less annoying, variant of Haskell's typeclasses. All it really does is take the Java interface system and remove explicit implementation, but, in so doing, it allows you to reverse the design. Rather than 'here's a type and it has this functionality', it's 'here's a function, and it wants anything that has this functionality'.</p>
<p>For a more heavy-duty overview of the difference between structure and functionality in Go's type system, see <a href="https://deedlefake.com/2017/07/the-problem-with-interfaces/" rel="nofollow">my blog</a>.</p></pre>Justinsaccount: <pre><p>I've found it's hard to get it right the first time. But once I do something like write an in memory backend and a boltdb backend, I can quickly realize what the interface should be.</p>
<p>One challenge for me is things like Pushdown filters and backend stores. If you had an App struct that contained a Store struct and you wanted to do something like</p>
<pre><code>App.FindById(10)
</code></pre>
<p>the sane way of doing that would be to have Store implement <code>FindById</code>. Then the App wouldn't even need to implement it directly. However, one thing I was just working on had a report as one of the functions, so it did something like</p>
<pre><code>func (a App) Report() myreport {
return a.store.DoReport()
}
</code></pre>
<p>but when I was working on refactoring some things and cleaning up the store interface I realized that the store should really just be returning all the data to App and store shouldn't concern itself on how the report is generated. It's slightly less efficient this way, but it keeps the store interface simpler and prevents every store implementation from having to implement the same report since the new function is something like</p>
<pre><code>func (a App) Report() myreport {
rawData := a.store.GetRawData()
report := lotsOfStuffToCalculateReport(rawData)
return report
}
</code></pre>
<p>Now backends just need to implement GetRawData, which is used in a few places anyway.</p>
<p>If performance became an issue I <em>could</em> just change the App to something like</p>
<pre><code>func (a App) Report() myreport {
if a.store.HasNativeReport {
return a.store.DoReport()
}
rawData := a.store.GetRawData()
report := lotsOfStuffToCalculateReport(rawData)
return report
}
</code></pre>
<p>Anyway, I think I got a bit off track. You should just watch</p>
<p><a href="https://www.youtube.com/watch?v=-wYLmsizBc0" rel="nofollow">https://www.youtube.com/watch?v=-wYLmsizBc0</a></p>
<p>which among other things does a good job of showing how interfaces may be easier to design the 2nd run through once you've made a mess of things.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
0 回复
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传