<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't want 3 separate setter methods and I don't want the world that "sets" 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("Favorite_food", Food{name:"Salmon"})</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 "simulate" 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 (
"fmt"
)
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("%+v", favorite)
}
func main() {
p := person{}
p.change(book{"The Old Man and the Sea"})
p.change(color{"red"})
p.change(food{"rice"})
}
</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 "update" 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("parmeggiano")
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 "fmt"
type Person map[string]interface{}
func (p *Person) updateAndTweet(attribute string, value interface{}, tweet func(string)) {
p[attribute] = value
tweet(fmt.Sprintf("My %v is %v.", attribute, value)
}
</code></pre></pre>clbanning: <pre><p>doesn'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
0 回复
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传