问题描述
我第一次使用 protobuf .proto 文件。
我有许多具有相同字段的模型:
message Player {
uint64 id = 1;
google.protobuf.Timestamp createdAt = 2;
google.protobuf.Timestamp updatedAt = 3;
string firstname = 4;
string lastname = 5;
//...
}
message Team {
uint64 id = 1;
google.protobuf.Timestamp createdAt = 2;
google.protobuf.Timestamp updatedAt = 3;
string name = 4;
//...
}
message League {
uint64 id = 1;
google.protobuf.Timestamp createdAt = 2;
google.protobuf.Timestamp updatedAt = 3;
string name = 4;
//...
}
...以及许多其他...
如您所见,我在每个结构中重复了相同的字段。
在这种情况下,DRY(不要重复自己)的最佳做法是什么?
我正在使用 Golang。
我可以像 Go 语言一样嵌入它们吗?
解决方法
您可以简单地使用经常重复的字段定义一个新消息,并将其用作其他消息中的字段类型:
message Metadata {
uint64 id = 1;
google.protobuf.Timestamp createdAt = 2;
google.protobuf.Timestamp updatedAt = 3;
}
message League {
Metadata metadata = 1;
string name = 2;
//...
}
在 Go 中,您将 Metadata
字段初始化为普通结构(包选择器将取决于您实际如何从 protobuffers 生成 Go 类型):
func newLeague() *grpcgen.League {
return &grpcgen.League{
Metadata: &grpcgen.Metadata{
Id: 1000,CreatedAt: ptypes.TimestampProto(time.Now()),UpdatedAt: ptypes.TimestampProto(time.Now()),}
Name: "foo",//...
}
}
编辑:
您认为这会减慢编码/解码过程吗
我们可以运行以下基准测试:
package foo
import (
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"foo/grpcgen"
"testing"
)
var withmeta = &grpcgen.LeagueWithMeta{
Metadata: &grpcgen.Metadata{
Id: 1000,CreatedAt: ptypes.TimestampNow(),UpdatedAt: ptypes.TimestampNow(),},Name: "foo",}
var nometa = &grpcgen.LeagueNoMeta{
Id: 1000,}
func BenchmarkEncProto(b *testing.B) {
b.Run("encode with meta",func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
b,_ := proto.Marshal(withmeta)
if b == nil {
panic("not marshaled")
}
}
})
b.Run("encode without meta",_ := proto.Marshal(nometa)
if b == nil {
panic("not marshaled")
}
}
})
}
$ go test -bench=. ./proto_benchmark_test.go -benchtime=10s
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
BenchmarkEncProto/encode_with_meta-4 13124650 948.8 ns/op 96 B/op 4 allocs/op
BenchmarkEncProto/encode_without_meta-4 25832161 417.0 ns/op 64 B/op 2 allocs/op
PASS
ok command-line-arguments 24.629s
Not 将字段包装在 Metadata
消息中具有更好的性能,这并不奇怪。实际上,这不会以明显的方式影响您的程序。
您可以使用通用字段创建消息:
message Meta {
uint64 id=1;
google.protobuf.Timestamp createdAt = 2;
google.protobuf.Timestamp updatedAt = 3;
}
message Player {
Meta meta=1;
string firstname = 4;
string lastname = 5;
//...
}
message Team {
Meta id = 1;
string name = 2;
//...
}
message League {
Meta meta=1;
string name = 2;
//...
}