【发布时间】:2019-05-31 01:19:47
【问题描述】:
我有一个数据类,它使用构建器创建对象并将数据以序列化形式存储在缓冲区中。我计划更改课程以添加和删除一些字段。有些系统将使用该类的两个版本来创建数据,即具有所有字段的当前版本和具有删除/添加字段的较新版本。我想看看最好的方法是什么,以便向后兼容(不破坏任何消费者)?
我有几个关于如何做到这一点的建议,但我很难选择其中一个。
要求:
存储的数据必须是二进制的。
两个版本的序列化记录长度相同
现有代码
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: 在构建器中添加新字段并在标头中使用版本来决定在构建时(在构建方法中)使用哪些字段来创建对象。这种方法的问题是所有方法都将在编译时存在于消费者,除非他们测试他们的代码,否则每个对象都是有效的。因此,在构建时很难推断出哪个版本需要哪些字段。
解决方案 2: 在类中添加一个具有所需字段的新构建器。现有构建器中将存在重复的字段。 然后,消费者可以使用他们想要的构建器。这似乎更干净,因为构建器将完全独立。这种方法的问题在于,由于我们正在添加和删除字段,因此字段将处于不同的偏移量,因此 getter 必须更改以使用基于 versionType 的偏移量。这对于未来的版本也是有问题的,因为那时我们将拥有这个巨大的类,每个版本都有很多构建器和 getter 中的逻辑
解决方案 3: 创建一个扩展 A 并拥有自己的构建器的新类(假设是 B)。这样代码就更加模块化了。问题是现在需要在某个地方有一些逻辑来区分并知道要调用哪个构造函数。例如,如果消费者通过 base64 来获取对象 A,则需要确定它是哪个版本。
String encodedString = "someString form of A"
A a = new A(encodedString);
是否有推荐的方法来使用构建器模式对这些数据类进行编码,以使其既未来兼容又向后兼容。
【问题讨论】:
标签: java design-patterns serialization builder-pattern