A single setter for all attributes in a struct

xuanbao · · 337 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hello. There are many attributes in a struct that I want to be able to change from a single method.</p> <p>For instance, let is say we are dealing with a <code>person</code> struct with the attributes <code>Favorite_book</code>, <code>Favorite_food</code>, and <code>Favorite_color</code>. I want a single method that I can update one of those favorites and then tweet it to the world. </p> <p>I don&#39;t want 3 separate setter methods and I don&#39;t want the world that &#34;sets&#34; the favorites of a person to know that the person should tweet. I want the tweeting to be encapsulated within the person.</p> <p>Therefore I want 1 single setter method to set all of these attributes.</p> <p>The types of those attributes are different <code>Book</code>, <code>Color</code>, and <code>Food</code>. </p> <p>I want to be able to call a method like this <code>person.change(&#34;Favorite_food&#34;, Food{name:&#34;Salmon&#34;})</code> although passing a string for an attribute name seems bad coming from the Common Lisp world where we would pass a symbol.</p> <p>Is something like this possible?</p> <hr/>**评论:**<br/><br/>raff99: <pre><p>You can use functional options to &#34;simulate&#34; the behavior you want:</p> <pre><code>type Option function(p *Person) func FavoriteFood(food Food) Option { return func(p *Person) { p.Favorite_food = food } } func FavoriteColor(color Color) Option { return func(p *Person) { p.Favorite_color = color } } func (p *Person) Change(options ...Option) { for _, setOption := range options { setOption(p) } // finish up with p } // // use like this // p.Change(FavoriteFood(food), FavoriteColor(color)) </code></pre></pre>joncalhoun: <pre><p>It is possible with reflection but you would lose type safety and it is very anti-go. You would be better off writing 3 distinct functions or using code generation to create those functions.</p></pre>a_t-2: <pre><p><a href="https://play.golang.org/p/dhQSucYHw4O" rel="nofollow">https://play.golang.org/p/dhQSucYHw4O</a></p> <pre><code> package main import ( &#34;fmt&#34; ) type favorite byte type favorite_discriminant interface { is_favorite() favorite } type food struct{ name string } type color struct{ name string } type book struct{ name string } func (f food) is_favorite() favorite { return favorite_book } func (c color) is_favorite() favorite { return favorite_color } func (b book) is_favorite() favorite { return favorite_food } const ( favorite_book = iota favorite_color favorite_food ) type person struct { favorite_food, favorite_color, favorite_book favorite_discriminant } func (the_person person) change(favorite favorite_discriminant) { switch favorite.is_favorite() { case favorite_book: the_person.favorite_book = favorite the_person.tweet(favorite) case favorite_color: the_person.favorite_color = favorite the_person.tweet(favorite) case favorite_food: the_person.favorite_food = favorite the_person.tweet(favorite) } } func (the_person person) tweet(favorite favorite_discriminant) { fmt.Printf(&#34;%+v&#34;, favorite) } func main() { p := person{} p.change(book{&#34;The Old Man and the Sea&#34;}) p.change(color{&#34;red&#34;}) p.change(food{&#34;rice&#34;}) } </code></pre></pre>peterbourgon: <pre><p>This is at once astonishing and horrifying, well done.</p> <p>To the OP, your requirements are wack.</p></pre>a_t-2: <pre><p>I could have use type switch inside the function</p> <pre><code>switch favorite.(type) </code></pre> <p>I could have replaced attributes with a map</p> <pre><code> type person struct { favorites map[favorite]favorite_discriminant } </code></pre> <p>as well. there are definitely design issues at first place, probably a case of XY problem.</p> <p><a href="https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem" rel="nofollow">https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem</a></p></pre>TinyBirdperson: <pre><p>[edit] did not understand the question correctly.</p> <p>You could pass a kind of &#34;update&#34; as a struct and only apply non nil values.</p> <pre><code>type PersonUpdate struct { NewFavoriteBook *Book NewFavoriteFood *Food } func (p *Person) Update(update PersonUpdate) error { if update.NewFavoriteBook != nil { p.FavoriteBook = *update.NewFavoriteBook } if update.NewFavoriteFood != nil { p.FavoriteFood = *update.NewFavoriteFood } return p.tweetAboutUpdate() } </code></pre></pre>SlightlyCyborg: <pre><p>The method has to accept multiple types as a parameter.</p></pre>TinyBirdperson: <pre><p>Yes, sorry, i didnt read the question correctly and thought you wanted something like <code>Update(x, y int, z bool)</code> - which is still possible but not as nice as the version with the extra struct.</p></pre>recurrency: <pre><p>One option is to define an interface like this:</p> <p><code>type FavouriteSetter interface { isFavourite() }</code></p> <p>and then have the <code>Food</code> type implement it:</p> <p>`type Food string</p> <p>func (f Food) isFavourite() {}`</p> <p>and then you can pass it like so:</p> <p><code>func main() { var person Person food := Food(&#34;parmeggiano&#34;) person.SetFavourite(food) }</code></p> <p>And type switch on it and set the appropriate field:</p> <p>`func (p *person) SetFavourite(s FavouriteSetter) {</p> <pre><code>switch v := s.(type) { case Food: p.Food = v </code></pre> <p>... `</p> <p><a href="https://play.golang.org/p/zqeHQ7hRFUh" rel="nofollow">https://play.golang.org/p/zqeHQ7hRFUh</a></p></pre>scottjbarr: <pre><p>Why not have a function that accepts a Favourites struct, and returns the difference? You can apply the changes and take whatever action you want. If anything has changed you can react however you want. No reflection needed, I like the type safety so I would rather have a few extra lines of code.</p> <p><a href="https://play.golang.org/p/miB9NpI1LUN" rel="nofollow">https://play.golang.org/p/miB9NpI1LUN</a></p> <p>I would probably go about this it a different way myself though.</p></pre>lespritd: <pre><p>What you want is a hashmap</p> <pre><code>import &#34;fmt&#34; type Person map[string]interface{} func (p *Person) updateAndTweet(attribute string, value interface{}, tweet func(string)) { p[attribute] = value tweet(fmt.Sprintf(&#34;My %v is %v.&#34;, attribute, value) } </code></pre></pre>clbanning: <pre><p>doesn&#39;t need to be complicated: <a href="https://play.golang.org/p/gh8xxTK2HY7" rel="nofollow">https://play.golang.org/p/gh8xxTK2HY7</a></p></pre>

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

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