协议缓冲区和OO设计

时间:2012-08-02 08:19:06

标签: oop protocol-buffers

我在客户端 - 服务器架构中使用协议缓冲区作为有线数据格式。域对象(java bean)将经历以下生命周期。

  1. 用于客户端业务逻辑
  2. 转换为protobuf格式
  3. 传输到服务器
  4. 转换回域对象
  5. 用于服务器端业务逻辑
  6. 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格式。

    当原型定义复杂且嵌套时,访问proto builder类getter / setter方法时是否存在性能问题?

2 个答案:

答案 0 :(得分:9)

我没有使用协议缓冲区的经验,但我建议实现针对特定序列化/传输框架量身定制的域对象。你可能会后悔。

软件应用程序的域对象和逻辑应尽可能独立于特定的实现问题(在您的情况下是序列化/传输),因为您希望您的域易于理解并且将来可重用/可维护。

如果要独立于序列化/传输定义域对象,则有两个选项:

  1. 在序列化/传输之前,将信息复制到协议 缓冲特定对象并将其发送到您的服务器。在那里 必须将信息复制回您的域对象。
  2. 使用非协议序列化库,如KryoProtoStuff直接将您的域对象转移到 服务器
  3. 选项1的缺点是您的域被定义了两次(这对于修改是不可取的)和信息的复制(这会产生容易出错且不可维护的代码)。

    选项2的缺点是你丢失schema evolution(虽然ProtoStuff显然是supports it)并且完整的(可能很大的)对象图被序列化和传输。虽然您可以在序列化/传输之前修剪对象图(手动或使用JGT)。

答案 1 :(得分:2)

我们制作了protobuf-converter来解决将您的域模型对象转换为Google Protobuf消息的问题,反之亦然。

如何使用

必须转换为protobuf消息的域模型类必须满足条件:

  • 类必须由包含的 @ProtoClass 注释标记 关于相关protobuf消息类的参考。
  • 类字段必须由 @ProtoField 注释标记。这些字段必须包含getter和setter。

E.g:

@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);

对象列表的转换类似于单个对象转换。