Why do people write very long struct definitions?

polaris · · 511 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>By very long I mean more than 10 fields. I am reading several open source projects, and I found that long struct definitions were very common.</p> <p>For example, the definition of <code>diskQueue</code> in <code>nsq</code> is more than 40 lines long.</p> <p>And there seems to be natural groupings, the authors would group some fields together and commented that these are about &#34;states&#34;, those are about &#34;time&#34;.</p> <p>Why don&#39;t they declare these &#34;state&#34; and &#34;time&#34; structs and embed them inside? What are the differences?</p> <hr/>**评论:**<br/><br/>TheMerovius: <pre><blockquote> <p>What are the differences?</p> </blockquote> <p>Less types (i.e. cleaner namespace) for one. Also, why would I separate inseparable concepts? If the fields only ever appear in one struct, why would I break them out into a separate one? That a struct definition is &#34;long&#34; (I don&#39;t consider 40 lines long. That&#39;s about half a screen on my setup) isn&#39;t an actual problem, so I don&#39;t see why one would need to solve it.</p></pre>weberc2: <pre><p>I never find myself writing structs with more than a few fields (in any language). Perhaps it&#39;s possible that all 40 fields serve a single responsibility, but I&#39;ve never seen this in practice. I suspect these structs are violating the single responsibility principle. This isn&#39;t endorsing the SRP and you&#39;re free to disagree with it; however, it likely shapes what the OP has come to think of as &#34;well written code&#34;.</p></pre>TheMerovius: <pre><p>40 lines is not the same as 40 fields. The original posting mentions &#34;more than 10 fields&#34;, which I find entirely reasonable.</p></pre>weberc2: <pre><p>10 fields is indeed more reasonable than 40 fields, but it&#39;s still the upper bound for SRP-respecting code in my experience.</p></pre>satorulogic: <pre><p>You don&#39;t break things into modules just for reusability, more often it&#39;s about readability.</p> <p>Yes, with a big enough screen and a small enough font-size, even 400 lines long is not a big deal, but is it easier to keep in mind than a smaller well organized struct? I don&#39;t think so.</p></pre>TheMerovius: <pre><blockquote> <p>more often it&#39;s about readability.</p> </blockquote> <p>I don&#39;t consider this impeding readability at all. I would not flag it in code review and would be comfortable writing that code myself. I would even argue that by putting everything in one struct, Readability is improved, as you need less skipping around.</p> <blockquote> <p>Yes, with a big enough screen and a small enough font-size, even 400 lines long is not a big deal</p> </blockquote> <p>yes, 400 lines long <em>is</em> a big deal. 40 lines isn&#39;t. This is independent of the screen size (I mentioned the screen size to emphasize that this length is <em>always</em> in my working set).</p></pre>peterbourgon: <pre><p>If there is some other benefit to grouping fields into another sub-struct, like maybe you can define some of your methods on that sub-struct instead of the parent, then it may start making sense. But modularization just to keep definitions smaller is probably premature abstraction. </p></pre>satorulogic: <pre><p>I think we can think about this in the same way we think about object oriented design. We should not use inheritance just to reuse code, we should use it to express &#34;is a&#34; relationships. Similarly, we don&#39;t use composition just to modularize things, we use it only when it helps us express &#34;has a&#34; relationships.</p></pre>TheMerovius: <pre><blockquote> <p>I think we can think about this in the same way we think about object oriented design. </p> </blockquote> <p>Hugely overrated and killing productivity and understandability? :) (sorry for the snark. You happened to mention OOP while I am in a phase where I need to write lots of Java and am frustrated by it&#39;s culture of throwing wrenches into programmers intentions)</p></pre>satorulogic: <pre><p>I&#39;m sorry to hear that you have to use languages that impose OOP on users. But I think, like many other things in the world, the question is not whether OOP is good or bad for productivity. Chances are it can be very helpful in some situations but hurt productivity in other situations.</p> <p>I&#39;m not argueing that one should encapsulate as much as possible and extract tons of sub-structs to shorten a struct definition. I think we all agree that, longer and more information to keep in mind is worse than shorter and less information.</p> <p>Now what we disagree? Whether 40+ lines a struct is too long.</p> <p>I don&#39;t know. I hope I can get used to this someday.</p></pre>peterbourgon: <pre><p>Yes, that makes sense to me.</p> <blockquote> <p>Now what we disagree? Whether 40+ lines a struct is too long.</p> </blockquote> <p>There is no magic number of lines, beyond which a struct is definitely too big. 40 lines may be fine for some piece of code. 200 lines may be fine. Probably 1000 lines is too many ;)</p></pre>I_HATE_SPIES: <pre><blockquote> <p>You don&#39;t break things into modules just for reusability, more often it&#39;s about readability.</p> </blockquote> <p>No! Spreading stuff all over the place doesn&#39;t make it clearer.</p></pre>satorulogic: <pre><p>Why is this the case?</p> <p>You don&#39;t really care about which fields are about states or meta every time you scan through the definition of the main struct. And we don&#39;t just split the fields and throw them away, we can organize them into structs with meaningful names. What&#39;s more, when reading the code, the editor can help us find the definition of the contained struct if we are indeed interested.</p></pre>thepciet: <pre><p>Its tradeoffs - after spending much time on files with 1k+ or 10k+ rows there is definitely incentive to spread out. But then when there are tons of one-off files or a type definition that points to twenty different other type-defining files that is also not great.</p> <p>Given enough experience, things like verbose commenting or extreme abstraction tend to get in the way of just getting it done. But so does repeatedly writing out behavior or data that could be abstracted. This is the art of software engineering and is not a golang-specific issue. You should try spreading out and see where you end up.</p></pre>satorulogic: <pre><p>Note that you don&#39;t have to spread out the structs in different files.</p> <p>You mentioned verbose commenting, that&#39;s what happens when you put too many fields in a struct, you start to add comments explaining &#34;the following block of fields is about the status&#34;.</p></pre>hansdr: <pre><p>I personally think that writing a large struct with well commented groupings is very readable. </p> <p>In some cases it can even be preferable to keep everything in one place. The field groups may have interdependencies. Plus, oversubdividing just fragments things into more files without adding any value.</p></pre>PsyWolf: <pre><p>You can have subtypes inside a larger struct without polluting the global namespace simply by using anonymous structs. That makes it clear that these fields are related while also making it clear that they don&#39;t appear anywhere outside of the parent struct. Win/win.</p> <p><a href="https://play.golang.org/p/0UFSGyCKXf" rel="nofollow">https://play.golang.org/p/0UFSGyCKXf</a></p></pre>weberc2: <pre><p>Except initializing those structs remains tedious. :(</p></pre>PsyWolf: <pre><p>Very true.</p></pre>TheMerovius: <pre><blockquote> <p>Win/win</p> </blockquote> <p>eh ¯\<em>(ツ)</em>/¯</p> <p>I would call it a loose/neutral. Readability suffers in my eyes and it doesn&#39;t bring any benefit.</p> <p>Which is to say (just to make it explicit): All of this is simply subjective. Whether you find struct{}&#39;s littered everywhere readable or not, whether you think 40 lines is too long and where you draw the line… all is subjective. Though I guess, what OP found is, that a lot of people agree with my notions (as he complains that struct definitions with my notions are &#34;very common&#34;).</p></pre>PsyWolf: <pre><p>Yeah, it&#39;s very subjective.</p></pre>Redundancy_: <pre><p>There are a number of fields in there which mention &#39;read&#39; or &#39;write&#39; which are used fairly cohesively, and quite frequently with the pattern of Close and nil (among other manipulations that look like need to be done carefully on multiple fields at once). I think you might be overstating the inseparability of concepts.</p></pre>hansdr: <pre><p>With some projects the reason may simply be that this is how the structures grew over time. A structure may start out small, and grow as the code has to handle more and more complexity.</p> <p>As I said in a separate comment, I think that writing a large struct with well commented groupings is very readable. Of course, if a structure has grown into a large and ugly mess, then it may be time to refactor the code.</p></pre>dasacc22: <pre><p>the other case here not asked in the question are anonymous structs in place of say, camelCasing or short names. I did this recently to see how it felt where (for opengl) i have a series of attributes, uniforms, and buffers. Each series member relates to a distinct thing, such as vertices.</p> <p>It becomes a case of either typing something like <code>x.attribVertex</code> vs <code>x.attribs.vertex</code>. I&#39;m still undecided with how it feels but I&#39;m likely to revert to the former or swap the words for better coherency, e.g. <code>x.vertex.attrib</code>.</p> <p>Still, it&#39;s a question I started asking myself recently.</p></pre>PsyWolf: <pre><p>Assuming you&#39;re using some kind of autocomplete in your editor, <code>x.attribs.vertex</code> would be accessible by typing <code>x.a&lt;tab&gt;.v&lt;tab&gt;</code> while the other form would require <code>x.attribv&lt;tab&gt;</code>.</p></pre>dasacc22: <pre><p>I do, but in this case <code>x.a&lt;ctrl+s&gt;v</code> would complete <code>x.attribVertex</code> for me just as well.</p></pre>hayzeus: <pre><p>I generally only break structures out like this when the embedded part would stand alone somewhere, or might be composed into something else. So for example, I might have a structure that represents a request to do something, but need that data in another structure that actually manages the performing of the request. So like:</p> <pre><code>type JobRequest struct { } type Job struct { JobRequest } </code></pre> <p>Otherwise, I don&#39;t find that multiple structures are more readable than a single larger structure (its actually the opposite for me). Obviously, though, this depends on whether the proposed substructure stands on its own. If it does, then I&#39;ll break it out</p></pre>thepciet: <pre><p>Here&#39;s a type I&#39;ve been working on:</p> <pre><code>package main ... type HTTPHost struct { HTML5Host // hosts and templates HTML/CSS/JavaScript/WebSocket AuthorizationHost // manages session keys and login persistence/security ProfileHost // information about users } </code></pre> <p>These current embedded fields are interfaces, so I can wire in whatever I need for a specific use of my application. But in reality there probably isn&#39;t much value to this, as my main HTTP host may always host the same HTML5 files without interpretation or always authorize in the same way and read from the same profile server.</p> <p>Point being that my nice clean small struct is abstracting away major implementations details that may be better as just included directly in my super HTTPHost struct. If I need my application to be a multi-tool then this abstraction is great, but now a new human coder will have to dig all over the file(s) to see the data implementation and how specific behavior is decided. Even worse if this is a complex well-tested piece of code that needs rare narrow changes, now that maintainer have to relearn all of these abstractions every time.</p></pre>thepciet: <pre><p>Another issue with abstraction is the potential for debugging nightmares. Stack traces with many layers of abstraction can be a slower unroll, or sometimes even impossible to see important info (which HTML5Host was in place at that time?). Careful logging and debugging strategies are hard won, and complex type systems can get in the way, hence a reason to make a huge struct to just dump with +v once.</p></pre>acron5n5: <pre><p>lol maybe because those fields are needed to reflect the object represented by the struct? And what you think is so good in having multyple nested structs? that will make the code &#34;dotty&#34;, you will have to write like &#34;s.f1.f2.f3.f4&#34; and so on all the day.</p></pre>kromem: <pre><p>Only if you have name clashes. Otherwise the parent struct can just access the embedded fields using a single dot and the name.</p></pre>weberc2: <pre><p>Most of the other answers seem to presume that the struct serves exactly one responsibility or that a struct serving multiple responsibilities is not a problem. In practice, I&#39;ve never seen a responsibility that requires 40 fields, but it&#39;s possible that it exists and that you&#39;re seeing them (though I think this is unlikely). More likely, the author of the code doesn&#39;t know about or disagrees with the Single Responsibility Principle, or perhaps he knows about and agrees with it but thought it could be sacrificed for the sake of time. These are the possible explanations I can see.</p></pre>TheRealHellcat: <pre><p>I just started coding in Go, and I have a central struct that also has all of the functions &#34;attached&#34; (i.e. all functions in that package get the struct as a receiver) and it keeps growing &#39;cause I collect all configuration and runtime information in it, and those things just belong together, it&#39;s one &#34;unit&#34;....</p> <p>That&#39;s why my struct might end up rather sized in the end.</p></pre>TheDukeOfAnkh: <pre><p>Sounds like a review of your design might be needed.</p></pre>TheRealHellcat: <pre><p>Care to elaborate, i.e. why it is bad to have all &#34;needed almost everywhere&#34; data in a central struct? Also all the &#34;attached&#34; functions need data from the struct, so I kinda have to give it to them as receiver (I think, don&#39;t I?)</p> <p>The struct is basically what a class would have been if I&#39;d have done this in a language that supports OOP.</p></pre>mekanikal_keyboard: <pre><p>if you have a beef with a particular github repo, why not just open an issue for them?</p></pre>nosmileface: <pre><p>Because.. enterprise.. That&#39;s how things roll in real life. :D</p></pre>

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

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