文章摘要
Protobuffers因其设计缺陷而受到批评,尤其是其糟糕的类型系统和复杂的编译过程。尽管由Google开发,但其设计问题不仅限于序列化抽象,还会影响代码质量。作者曾在Google工作,认为这些问题并非使用不当,而是源于其本身的设计缺陷。
文章总结
Protobuffers的问题与设计缺陷
Protobuffers作为一种序列化工具,虽然被广泛使用,但其设计存在诸多问题,尤其是在类型系统和代码生成方面。本文详细分析了Protobuffers的缺陷,并指出其在实际应用中的负面影响。
1. 类型系统设计不佳
Protobuffers的类型系统被认为是“业余”设计的产物,类似于Java的类型系统,既不满足动态类型支持者的需求,也不满足静态类型支持者的期望。其类型系统的设计缺乏原则性,许多功能像是后期拼凑而成,导致了一系列不合理的限制。例如:
oneof字段不能是repeated。map<k,v>字段的键和值有专门的语法,但不适用于其他类型。- 用户定义的类型无法参数化,导致开发者不得不手动实现常见的数据结构。
map字段不能是repeated,且键不能是enum类型。
这些限制反映了Protobuffers在设计上的随意性,缺乏对现代类型系统的理解。
2. 缺乏组合性
Protobuffers的各个功能之间缺乏组合性,导致开发者在使用时面临诸多不便。例如,map字段在底层被解析为repeated Pair<k,v>,但由于repeated是一个关键字而非类型,它无法与自身组合。这种设计使得Protobuffers的类型系统显得笨拙且不灵活。
3. 不合理的设计选择
Protobuffers在标量类型和消息类型之间做出了不合理的区分。标量类型总是存在的,即使未设置也会被初始化为默认值,这使得无法区分未设置的字段和设置为默认值的字段。这种设计在API的向前和向后兼容性方面带来了噩梦般的问题。
此外,消息类型的行为也令人困惑。未设置的消息字段在访问时会返回一个默认初始化的副本,但修改该字段时会修改其父容器。这种行为违反了基本的编程原则,例如msg.foo = msg.foo;这样的赋值操作可能会意外地修改msg。
4. 兼容性的谎言
Protobuffers声称能够轻松实现向前和向后兼容的API,但实际上这种兼容性是通过默认执行错误操作来实现的。Protobuffers对数据的处理过于宽松,导致开发者必须在每个使用点编写防御性检查代码,以确保数据的合理性。这种做法将数据校验的逻辑分散到整个代码库中,而不是在反序列化阶段集中处理。
5. 对代码库的污染
Protobuffers的设计使得它们难以被限制在网络边界内,导致其在整个代码库中蔓延。开发者不得不在以下三种糟糕的选择中做出权衡:
- 维护一个单独的类型来描述实际需要的数据,并确保其与Protobuffers同步演化。
- 将丰富的数据打包到网络格式中以供应用程序使用。
- 每次需要时从简洁的网络格式中推导出丰富的信息。
第一种选择是最佳实践,但由于Protobuffers的类型系统不够强大,开发者不得不编写额外的序列化代码,这违背了使用Protobuffers的初衷。
结论
Protobuffers的设计缺陷使得它们在实际应用中带来了诸多问题,尤其是在类型系统和代码生成方面。其不合理的限制和设计选择不仅增加了开发者的负担,还污染了整个代码库。因此,除非有充分的理由,否则应避免在项目中使用Protobuffers。
评论总结
评论内容主要围绕Protocol Buffers(Protobuf)的优缺点展开,观点分为支持和反对两派。
支持Protobuf的观点: 1. 解决实际问题:Protobuf解决了序列化和反序列化的实际问题,尤其是在跨语言通信中表现良好。 - "Despite issues, protobufs solve real problems and (imo) bring more value than cost to a project."(评论3) - "Protobuf has undoubtedly, empirically proven its real value in real systems, despite its admittedly large number of warts."(评论28)
- 向后兼容性:Protobuf的向后兼容性设计使得系统升级更加灵活。
- "Protocol Buffers is the most widely used... being able to add fields to the schema and then deploy the servers and clients in any order is way nicer."(评论6)
- "Protobuf's single most important feature is the ability to add new fields over time while maintaining compatibility."(评论28)
反对Protobuf的观点: 1. 设计缺陷:Protobuf的设计被认为存在诸多问题,如枚举类型不能作为映射键、初始化语义不明确等。 - "The 'no enums as map keys' thing enrages me constantly."(评论7) - "the weird protobuf initialization semantics has caused so many OMGs."(评论30)
- 复杂性过高:对于不需要极端网络性能的项目,Protobuf可能带来不必要的复杂性。
- "In general, if you don’t have extreme network needs, then protobuf seems to cause more harm than good."(评论8)
- "I switched to JSON, HTTP 'REST' and websockets... and am as happy as i could be."(评论27)
中立观点: 1. 权衡利弊:尽管Protobuf存在缺陷,但在实际应用中仍然有其价值。 - "Worse is better. Protobuffs are a PITA, but they are widely used and getting new adoption in industry."(评论20) - "Protobuf is dated, it's not that hard to make better things."(评论21)
总结来看,Protobuf在解决跨语言通信和向后兼容性方面表现出色,但其设计缺陷和复杂性也引发了广泛批评。支持者认为其实用性大于缺点,而反对者则建议在不需要高性能的场景下使用更简单的替代方案如JSON。