用构建器模式编写数据类

时间:2019-05-29 23:41:25

标签: java design-patterns serialization builder-pattern

我有一个数据类,该数据类使用构建器创建对象并将数据以序列化形式存储在缓冲区中。我打算更改类以添加和删除一些字段。有些系统会使用该类的两个版本来创建数据,即具有所有字段的当前版本和具有已删除/添加的字段的较新版本。我正在尝试找出最佳方法是什么,以使其向后兼容(不破坏任何消费者)?

关于如何执行此操作,我有一些建议,但我很难选择一个。

要求:
存储的数据必须为二进制。
两种版本中序列化记录的长度相同

现有代码

public class A implements Comparable<A>, Serializable {

private final Buffer buffer;
public static final Builder {
  private Header header//header with version
  private long creationTime;
  private SomeObject someObject;//this is removed in next version
  private OtherObject otherObject;//this is added in next version

  public Builder() { }

 //bunch of getters setters for fields

  public A build() {return new A(this);}

  private A(Builder b) {
   //build the object and put into the buffer
   validate()
  }
  private void validate() {//validates the object}

  public A(Buffer buf) {
   this.buffer=buf;
   validate();
  }
  public A(String encodedString) {
   this(ByteBuffer.wrap(encodedString));
  }
}
// consumers use this to get creationTime for object A
public long getCreationTime() {
 return buffer.getLong(OFFSET_CREATION_DATE);
}
}

解决方案1::在构建器中添加新字段,并使用标题中的version来确定在构建时(在构建方法中)使用哪些字段来创建对象。这种方法的问题在于,所有方法都将在编译时提供给使用者,除非它们测试其代码,否则每个对象都是有效的。因此,很难在构建时就哪个字段需要哪个版本的原因进行推理。

解决方案2::在类中添加具有所需字段的新构建器。现有构建器中将有重复的字段。 消费者然后可以使用他们想要的构建器。这似乎更清洁,因为建筑商将完全独立。这种方法的问题在于,由于我们要添加和删除字段,因此字段将具有不同的偏移量,因此吸气剂将不得不更改以使用基于versionType的偏移量。这对于将来的版本也是有问题的,因为那时我们将拥有这个巨大的类,每个版本的getter中都有大量的构建器和逻辑

解决方案3::创建一个扩展A并具有自己的生成器的新类(比方说B)。这样,代码将更加模块化。问题在于,现在需要在某个地方添加一些逻辑以区分并知道要调用哪个构造函数。例如,如果使用者正在传递base64来获取对象A,则它将需要弄清楚它是哪个版本。

String encodedString = "someString form of A"
A a = new A(encodedString);

是否存在使用生成器模式对这些数据类进行编码的推荐方法,以使其与将来和向后兼容。

1 个答案:

答案 0 :(得分:1)

方法2 方法1 + 正确的二进制表示形式结合在一起就是答案。为您的二进制表示形式选择正确的格式,最简单的方法是选择json。为V1和V2对象制作具体的构建器,并使用字节缓冲区构造它们。每个建造者/工厂将只对它识别的字段感兴趣。如果可能会引发构建者/工厂尝试反序列化错误的版本异常,则可以考虑使用版本字段。具体的构建者/工厂将仅构建其可识别版本的对象。

在我看来,子类化是不必要的。您可以将Builder / factory类与object类分开。参见Hazelcast的“ StreamSerializer”示例,该类完全是专门用于编组的实体的外部类。

使用正确的格式将与方法2相比偏移来解决您的问题。如果您必须以二进制形式使用它,那么一种解决方法是使用扁平格式,其中记录大小大于基本记录大小,并且您保留了可用的更改空间。在以前的Cobol时代,这就是他们的做法。我不建议您这样做。使用json :)最简单的方法可能并不是最有效的方法。您还可以检查https://developers.google.com/protocol-buffers/协议缓冲区。

根据分拆时为序列化选择的布局,您可以配置责任序列,尝试对流部分进行反序列化。弃用版本时,封送拆封器将被从链中删除/停用。