【发布时间】:2018-01-08 02:15:36
【问题描述】:
我有一个系统可以解析一个包含 mysql 表变更集的日志文件,想想像 binlog 之类的东西。可以有更新和插入,我们现在忽略的删除。我的模块的函数得到这样的输入:
type Changeset struct {
Table string // which table was affected
Type string // INSERT or UPDATE
OldData map[string]string // these 2 fields contain all columns of a table row
NewData map[string]string
}
OldData 是 INSERT 变更集时为空,当它是 UPDATE 变更集时,OldData 和 NewData 被填充(更新前后的数据)。
现在我不想在我的模块中使用这样的无类型数据,因为我需要对一些域进行建模,并且具有一些类型安全性会更好。但是,如果更改是对该域逻辑的插入或更新,我仍然需要保留知识(例如,如果是更新,我将验证某些字段没有更改,例如)。
假设我有两张表(假设它们只有一个名为 Id 的字段,但实际上它们有更多不同的字段)。所以我像这样对这些对象进行建模:
type Foo struct { // foo table
Id string
// ... imagine more fields here ...
}
type Bar struct { // bar table
Id string
// ... imagine more fields here ...
}
现在我可以从Changeset.OldData 和Changeset.NewData 映射map[string][string],但是我不知道更改是插入还是更新。我来回考虑了一下,但我想出的最好的是:
type FooInsert struct {
New Foo
}
type FooUpdate struct {
New Foo
Old Foo
}
type BarInsert struct {
New Bar
}
type BarUpdate struct {
New Bar
Old Bar
}
映射代码如下所示:
func doMap(c Changeset) interface{} {
if c.Table == "foo" {
switch c.Type {
case "UPDATE":
return FooUpdate{Old: Foo{Id: c.OldData["id"]}, New: Foo{Id: c.NewData["id"]}}
case "INSERT":
return FooInsert{New: Foo{Id: c.NewData["id"]}}
}
}
if c.Table == "bar" {
switch c.Type {
// ... almost same as above, but return BarUpdate/BarInsert ...
}
}
return nil
}
好处是,它让我可以像这样写对这个映射函数的结果做一个类型切换:
insertChangeset := Changeset{
Table: "foo",
Type: "INSERT",
NewData: map[string]string{"id": "1"},
}
o := doMap(insertChangeset)
switch o.(type) {
case BarUpdate:
println("Got an update of table bar")
case FooUpdate:
println("Got an update of table foo")
case BarInsert:
println("Got an insert to table bar")
case FooInsert:
println("Got an insert to table foo")
}
typeswitch 是我最终需要的(每个更改集类型和每个实体都有不同的类型。)但是:
-
doMap中的映射代码非常丑陋且重复。 - 对于我引入的每个新实体
X,我需要再创建两个类型XInsert和XUpdate。
有什么办法可以解决这个烂摊子吗?在其他编程语言中,我可能会想到类似的东西:
type Update<T> {
T Old
T New
}
type Insert<T> {
T New
}
但不确定如何在 Go 中对此进行建模。我还创建了一个游乐场示例,它在一个程序中显示了整个代码:https://play.golang.org/p/ZMnB5K7RaI
【问题讨论】: