Alternative to using Getters and Setters?

polaris · · 1230 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;ve read mixed opinions about getters and setters in OOP. I couldn&#39;t find much go-specific information so I&#39;m turing to you, <a href="/r/golang">/r/golang</a>.</p> <p>On one hand, I don&#39;t want to have to write 2 methods for every field in a struct if I have to use getters and setters. It is much less work to just create the struct and then access the fields like normal.</p> <p>On the other hand, I want inheritance. I understand how I can incorporate field inheritance with structs, but I want functions to be able to take a struct object and accept structs that inherit fields from the parameter struct. Here is an example of what I want to do:</p> <pre><code>type Entity struct { Id uint64 Age uint64 } type Human struct { *Entity Name string } func PrintAge(entity Entity) { fmt.Println(entity.Age) } func main() { entity := Entity{0, 20} person := Person{&amp;Entity{1, 10}, &#34;ojbway&#34;} PrintAge(entity) PrintAge(person) } </code></pre> <p>Is there really no simple way of doing this without using a combination of an interface and getter/setter functions? I have a couple of solutions but they aren&#39;t super easy for me in the long run (adding more classes is more work than without doing this).</p> <p>I guess I just want your thoughts on this. Thanks! :)</p> <hr/>**评论:**<br/><br/>Ploobers: <pre><p>This is untested, but should work. We use this pattern all throughout our codebase.</p> <pre><code>type Entity struct { ID uint64 Age uint64 } type Human struct { Entity Name string } func (e Entity) PrintAge() { fmt.Println(e.Age) } func main() { entity := Entity{0, 20} person := Person{Entity{1, 10}, &#34;ojbway&#34;} entity.PrintAge() person.PrintAge() } </code></pre> <p>PS. I capitalized Id per Go standards</p></pre>ojbway: <pre><p>If we treat methods like fields, then this is just another form of field inheritance. I&#39;m talking about in the cases where I can&#39;t program a method and need a function (e.g. my function takes 2 parameters of struct types that are &#34;superclasses&#34; of something else I want to pass).</p> <p>The best solution that I could come up with was to have an interface that returned a pointer to the desired field. That way you could access and set something. For example:</p> <pre><code>type PersonInterface interface { ID() *uint64 Name() *string } type Person struct { id uint64 name string // private fields } func (person *Person) ID() *uint64 { return &amp;person.id } func (person *Person) Name() *string { return &amp;person.name } func main() { person := &amp;Person{0, &#34;bob&#34;} fmt.Println(*person.ID()) // 0 *person.ID() = 20 fmt.Println(*person.ID()) // 20 } </code></pre> <p>I did test this and it does in fact work. This way I only have 1 function for each field. Also I am able to write functions that take an argument of type PersonInterface, and then sub-interfaces of PersonInterface will also work.</p> <p>The problem is that I have to remember that when dealing with these kinds of objects, I am dealing with pointers. I&#39;m the type of person who will forget that and screw everything up.</p> <p>Is there no way for us to do the same thing with structs, and have structs inherit structs and guarantee that a value (even a zero-value) is present for the given field?</p> <p>edit: formatting</p></pre>Ploobers: <pre><p>In my experience, I ended up using Getters and Setters early on using Go, but I don&#39;t think they exist anywhere in my code now. I didn&#39;t specifically try to remove them, but as my coding style evolved, they became unnecessary.</p> <p>For all of my CRUD operations, I have some standard fields in a crud.Info struct, similar to your Entity object. I embed that in all of my other structs, like Client, Campaign, Dashboard, etc. That allows me to have a sort of reverse inheritance, just like my code sample works. </p> <p>When I need to perform functions on that, I use crud.Info as the parameter which gives me access to all its fields and functions without getters and setters.</p></pre>djherbis: <pre><p>Whenever I want to simplify something in Go, I look for a good interface. In this case, you want an easy way to access the &#34;Entity&#34; fields of a struct, regardless of the struct. Rather than exposing individual fields of Entity, just expose the whole entity object via an interface like so:</p> <p><a href="http://play.golang.org/p/ESX9S_Hpin">http://play.golang.org/p/ESX9S_Hpin</a></p> <p>Here&#39;s an extended version with three different approaches for PrintAge based on the same idea but with differently typed Args. <a href="http://play.golang.org/p/8rwry0PZRj">http://play.golang.org/p/8rwry0PZRj</a></p></pre>kpmy: <pre><p><a href="https://gist.github.com/kpmy/ba65960a40e41f004fbd" rel="nofollow">https://gist.github.com/kpmy/ba65960a40e41f004fbd</a> no Set/Get</p></pre>jerf: <pre><p>Generally speaking the answer is no different in Go than it is in any other language. Getters are a code smell and setters are an <em>enormous</em> code smell. You need to concentrate on what you want the object to <em>do</em>, not what the object <em>is</em>. Sometimes I end up with something that looks like a &#34;getter&#34; in my interfaces, but really it&#39;s just a case where it so happens that what I want to &#34;do&#34; is get something like a name or identifier or something, and there&#39;s no further meaning, but it&#39;s still conceptually not a &#34;getter&#34;, it&#39;s a <em>question</em> I&#39;m asking the object that happens to have a simple answer.</p> <p>Inheritance is not a thing in Go. Your example is almost too simple to work with, but what&#39;s going on here is that you have:</p> <pre><code>type HasAge interface { Age() uint64 } </code></pre> <p>And that&#39;s it. That&#39;s the fundamental thing going on here. You have a set of objects that you wish to ask about their age. It is an <em>implementation detail</em> that it so happens that many of the objects can have their &#34;age&#34; code factored out into an &#34;Age&#34; object:</p> <pre><code>type Age uint64 func (a Age) Age() uint64 { return uint64(Age) } type Human struct { .... Age } type NotHuman struct { .... Age } </code></pre> <p>but this is not &#34;inheritance&#34;. It&#39;s just a convenient refactoring.</p> <p>Basically, that&#39;s how you should approach Go OO design. Start with the thing using the objects. Figure out what methods it wants to call; there&#39;s always a set of such things. Pull that into an interface. Write a couple of implementations of the interface. If it so happens that some chunk of the interface can be cleanly factored into a subordinate object that can be <em>composed</em> in (key word here, not &#34;inherited&#34;), great, go ahead. If not, no stress, no problem.</p> <p>If you find yourself in a chunk of code that is &#34;getting&#34; and &#34;setting&#34;, step up a level (probably literally a block in the code), and see if you can move this logic into the interface itself. Again, your example code is too small here to provide a useful demonstration.</p> <p>In rare cases you may actually want a true &#34;inheritance&#34; hierarchy. In that case you&#39;ll have to do it manually, analogously to how languages that prefer to syntactically privilege inheritance require manual work to do what Go does automatically for composition. I haven&#39;t hit one of these yet, but I could name some cases where I&#39;d consider it. But in general, you&#39;ll find composition takes you quite a bit further than your inheritance-trained mental models are telling you. If you aren&#39;t doing &#34;some sort of UI widget hierarchy&#34;, you probably don&#39;t need inheritance.</p></pre>egonelbre: <pre><p>That example really doesn&#39;t make sense to me. Is there a real-world case that you are looking to solve. Basically, I probably wouldn&#39;t create such model, but I can&#39;t suggest anything better since the actual problem is missing.</p></pre>

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

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