因此,我刚开始接触Go,而我正在尝试清理Go-way中的错误。结果是拥有type Movie struct
的方法来更新记录并同步到数据存储。因此是一个示例方法:
func (movie Movie) SetTitle(title string) : error {
prevTitle := movie.Title
movie.Title = title
json, err := json.Marshal(movie)
if (err != nil) {
movie.Title = prevTitle
return err
}
err = db.SetValue(movie.id, json)
if (err != nil) {
movie.Title = prevTitle
return err
}
return nil
}
上面的问题是对失败操作的“清理”是保存先前的状态,并适当还原。但这感觉很难维护-将来很容易进行新的错误检查,而返回时却没有适当的清理就很容易。如果进行多次此类“清理”,情况将变得更加糟糕。
因此,相比之下,在其他语言中,我会将整个位包装在try / catch中,并在单个catch块内处理清理。在这里,我可以:
movie
的副本,执行副本上的所有操作,然后在一切成功后再复制回原始对象if json, err := json.Marshal(movie); err == nil {
if err = db.SetValue(...); err == nil {
return nil
}
}
movie.Title = prevTitle;
return err
哪种方法有效,但我不喜欢每张支票有一定的嵌套量。
func Update() : err
函数,以最大程度地减少所需的检查次数(仅想到这一点-认为我喜欢这一点)我觉得这些都不是完美的。我缺少明显的东西吗?
答案 0 :(得分:2)
您的坚持方式会导致许多问题:
我建议将更新和持久性分开。 Playground
type Movie struct {
id int
Title string
}
func (m *Movie) Persist() error {
json, err := json.Marshal(m)
if err != nil {
return err
}
log.Printf("Storing in db: %s", json)
return nil
}
func main() {
m := &Movie{1, "Matrix"}
m.Title = "Iron Man"
if err := m.Persist(); err != nil {
log.Fatalln(err)
}
}
在您的示例中,您使用按值接收器。在这种情况下,您可以在方法中获得该结构的副本,可以随意修改该副本,但是所有更改在外部都不可见。
func (movie Movie) SetTitleValueSemantic(title string) error {
movie.Title = title
json, err := json.Marshal(movie)
if err != nil {
return err
}
log.Printf("Serialized: %s", json)
return nil
}
playgorund:https://play.golang.org/p/mVnQ66TCaG9
我强烈建议您避免使用这种编码样式。 如果您真的需要这种东西,请参考以下示例:
游乐场:https://play.golang.org/p/rHacnsRLkEE
func (movie *Movie) SetTitle(title string) (result *Movie, e error) {
movieCopy := new(Movie)
*movieCopy = *movie
result = movie
defer func() {
if e != nil {
result = movieCopy
}
}()
movie.Title = title
serialized, e := json.Marshal(movie)
if e != nil {
return
}
log.Printf("Serialized: %s", serialized)
return
}
答案 1 :(得分:1)
Alexey是正确的,但是如果Movie
结构具有指针或切片字段,则不会复制它们。
下面是一个示例,该示例在每次更新之前手动复制切片字段(Tags
),并且还具有一个不错的事务处理方法(update
),我认为您可以使用:
type Movie struct {
id int
Title string
Year int
Tags []string
}
func (m *Movie) update(fn func(m *Movie) error) error {
// Make a copy of Movie.
movieCopy := *m
// Manually copy slice and pointer fields.
movieCopy.Tags = make([]string, 0, len(m.Tags))
copy(movieCopy.Tags, m.Tags)
// Run the update transaction on the copy.
if err := fn(&movieCopy); err != nil {
return err
}
// Save to db.
data, err := json.Marshal(movieCopy)
if err != nil {
return err
}
return db.SetValue(m.id, data)
}
func (m *Movie) SetTitle(title string) error {
m.update(func(mm *Movie) error {
mm.Title = title
return nil
})
}
func (m *Movie) SetYear(year int) error {
m.update(func(mm *Movie) error {
mm.Year = year
return nil
})
}