协议缓冲区和 OO 设计
Posted
技术标签:
【中文标题】协议缓冲区和 OO 设计【英文标题】:Protocol buffer and OO design 【发布时间】:2012-08-02 08:19:06 【问题描述】:我在客户端-服务器架构中使用协议缓冲区作为有线数据格式。域对象(java bean)将经历以下生命周期。
-
用于客户端业务逻辑
转换为 protobuf 格式
传输到服务器
转换回域对象
用于服务器端业务逻辑
ProtoBuf 文档中的"Protocol Buffers and O-O Design" 部分建议将生成的类包装在适当的域模型中。
我想找出最好的方法。
例如我有一个简单的原型定义。
package customer;
option java_package = "com.example";
option java_outer_classname = "CustomerProtos";
message Customer
required string name = 1;
optional string address = 2;
这就是定义域模型的方式。如您所见,数据完全存储在 proto builder 对象中。
package com.example;
public class CustomerModel
private CustomerProtos.Customer.Builder builder = CustomerProtos.Customer.newBuilder();
public String getName()
return builder.getName();
public void setName(String name)
builder.setName(name);
public String getAddress()
return builder.getAddress();
public void setAddress(String address)
builder.setAddress(address);
public byte[] serialize()
return builder.build().toByteArray();
这是一个好习惯吗?因为这些对象在生命周期的各个阶段都会用到,而我们只在客户端-服务器传输阶段需要protocolbuf格式。
在原型定义复杂且嵌套的情况下,访问原型构建器类的 getter/setter 方法时是否存在任何性能问题?
【问题讨论】:
你找到了解决这个问题的好方法吗(proto vs OO)? 【参考方案1】:我没有使用协议缓冲区的经验,但我不建议实施为特定序列化/传输框架量身定制的域对象。你以后可能会后悔。
软件应用程序的域对象和逻辑应尽可能独立于特定的实现问题(在您的情况下是序列化/传输),因为您希望您的域易于理解并且将来可重用/可维护。
如果您想定义独立于序列化/传输的域对象,您有两种选择:
-
在序列化/传输之前,将信息复制到协议
缓冲特定对象并将它们发送到您的服务器。你在那里
必须将信息复制回您的域对象。
使用非协议序列化库,如Kryo 或
ProtoStuff 直接将您的域对象转移到
服务器。
选项 1 的缺点是您的域被定义了两次(这对于修改来说是不可取的)和信息的复制(这会产生容易出错和不可维护的代码)。
选项 2 的缺点是您丢失了 schema evolution(尽管 ProtoStuff 显然是 supports it)并且完整的(可能很大)对象图被序列化和传输。虽然您可以在序列化/传输之前修剪对象图(手动或使用JGT)。
【讨论】:
完全同意@Barry。如果将相同的 protobuf 生成的 pojo 用作构建逻辑的 domian 对象,它很快就会变得混乱。正如所建议的,它基本上只是为了方便的服务-服务通信,与语言无关,仅此而已。有像 wrapper 这样的解决方案,但是为每个 protobuf 对象加上 imgine wrapper,这完全是一团糟。在我看来,最好为每个 proto 使用转换器工具或使用任何映射器库(如果有)。【参考方案2】:我们创建了protobuf-converter 来解决将域模型对象转换为 Google Protobuf 消息的问题,反之亦然。
如何使用:
必须转化为protobuf消息的领域模型类必须满足条件:
类必须由 @ProtoClass 注释标记,其中包含 相关 protobuf 消息类的参考。 类字段必须由 @ProtoField 注释标记。这些字段必须有 getter 和 setter。例如:
@ProtoClass(ProtobufUser.class)
public class User
@ProtoField
private String name;
@ProtoField
private String password;
// getters and setters for 'name' and 'password' fields
...
将用户实例转换为相关protobuf消息的代码:
User userDomain = new User();
...
ProtobufUser userProto = Converter.create().toProtobuf(ProtobufUser.class, userDomain);
后向转换代码:
User userDomain = Converter.create().toDomain(User.class, userProto);
对象列表的转换类似于单个对象的转换。
【讨论】:
是否有适用于 Dart Lang 的等效解决方案?或者,出现了任何新的最佳实践来转换消息/域对象,反之亦然?以上是关于协议缓冲区和 OO 设计的主要内容,如果未能解决你的问题,请参考以下文章